23 using namespace clang;
28 class BlockInCriticalSectionChecker :
public Checker<check::PostCall> {
32 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
33 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
34 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
36 StringRef ClassLockGuard, ClassUniqueLock;
38 mutable bool IdentifierInfoInitialized;
40 std::unique_ptr<BugType> BlockInCritSectionBugType;
42 void initIdentifierInfo(
ASTContext &Ctx)
const;
44 void reportBlockInCritSection(
SymbolRef FileDescSym,
46 CheckerContext &C)
const;
49 BlockInCriticalSectionChecker();
51 bool isBlockingFunction(
const CallEvent &Call)
const;
52 bool isLockFunction(
const CallEvent &Call)
const;
53 bool isUnlockFunction(
const CallEvent &Call)
const;
58 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
65 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
66 : IILockGuard(nullptr), IIUniqueLock(nullptr),
67 LockFn(
"lock"), UnlockFn(
"unlock"), SleepFn(
"sleep"), GetcFn(
"getc"),
68 FgetsFn(
"fgets"), ReadFn(
"read"), RecvFn(
"recv"),
69 PthreadLockFn(
"pthread_mutex_lock"),
70 PthreadTryLockFn(
"pthread_mutex_trylock"),
71 PthreadUnlockFn(
"pthread_mutex_unlock"),
73 MtxTimedLock(
"mtx_timedlock"),
74 MtxTryLock(
"mtx_trylock"),
75 MtxUnlock(
"mtx_unlock"),
76 ClassLockGuard(
"lock_guard"),
77 ClassUniqueLock(
"unique_lock"),
78 IdentifierInfoInitialized(
false) {
80 BlockInCritSectionBugType.reset(
81 new BugType(
this,
"Call to blocking function in critical section",
85 void BlockInCriticalSectionChecker::initIdentifierInfo(
ASTContext &Ctx)
const {
86 if (!IdentifierInfoInitialized) {
92 IILockGuard = &Ctx.
Idents.
get(ClassLockGuard);
93 IIUniqueLock = &Ctx.
Idents.
get(ClassUniqueLock);
94 IdentifierInfoInitialized =
true;
98 bool BlockInCriticalSectionChecker::isBlockingFunction(
const CallEvent &Call)
const {
99 if (Call.isCalled(SleepFn)
100 || Call.isCalled(GetcFn)
101 || Call.isCalled(FgetsFn)
102 || Call.isCalled(ReadFn)
103 || Call.isCalled(RecvFn)) {
109 bool BlockInCriticalSectionChecker::isLockFunction(
const CallEvent &Call)
const {
110 if (
const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
111 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
116 if (Call.isCalled(LockFn)
117 || Call.isCalled(PthreadLockFn)
118 || Call.isCalled(PthreadTryLockFn)
119 || Call.isCalled(MtxLock)
120 || Call.isCalled(MtxTimedLock)
121 || Call.isCalled(MtxTryLock)) {
127 bool BlockInCriticalSectionChecker::isUnlockFunction(
const CallEvent &Call)
const {
128 if (
const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
129 const auto *DRecordDecl = dyn_cast<
CXXRecordDecl>(Dtor->getDecl()->getParent());
131 if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
135 if (Call.isCalled(UnlockFn)
136 || Call.isCalled(PthreadUnlockFn)
137 || Call.isCalled(MtxUnlock)) {
143 void BlockInCriticalSectionChecker::checkPostCall(
const CallEvent &Call,
144 CheckerContext &
C)
const {
145 initIdentifierInfo(C.getASTContext());
147 if (!isBlockingFunction(Call)
148 && !isLockFunction(Call)
149 && !isUnlockFunction(Call))
153 unsigned mutexCount = State->get<MutexCounter>();
154 if (isUnlockFunction(Call) && mutexCount > 0) {
155 State = State->set<MutexCounter>(--mutexCount);
156 C.addTransition(State);
157 }
else if (isLockFunction(Call)) {
158 State = State->set<MutexCounter>(++mutexCount);
159 C.addTransition(State);
160 }
else if (mutexCount > 0) {
161 SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
162 reportBlockInCritSection(BlockDesc, Call, C);
166 void BlockInCriticalSectionChecker::reportBlockInCritSection(
168 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
173 llvm::raw_string_ostream os(msg);
174 os <<
"Call to blocking function '" << Call.getCalleeIdentifier()->getName()
175 <<
"' inside of critical section";
176 auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
177 R->addRange(Call.getSourceRange());
178 R->markInteresting(BlockDescSym);
179 C.emitReport(std::move(R));
182 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
183 mgr.registerChecker<BlockInCriticalSectionChecker>();
186 bool ento::shouldRegisterBlockInCriticalSectionChecker(
const LangOptions &LO) {
const SymExpr * SymbolRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
This class represents a description of a function call using the number of arguments and the name of ...
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy...
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Dataflow Directional Tag Classes.
Represents a C++ struct/union/class.