24 using namespace clang;
29 class BlockInCriticalSectionChecker :
public Checker<check::PostCall> {
33 CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
34 PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
35 MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
37 StringRef ClassLockGuard, ClassUniqueLock;
39 mutable bool IdentifierInfoInitialized;
41 std::unique_ptr<BugType> BlockInCritSectionBugType;
43 void initIdentifierInfo(
ASTContext &Ctx)
const;
45 void reportBlockInCritSection(
SymbolRef FileDescSym,
46 const CallEvent &call,
47 CheckerContext &C)
const;
50 BlockInCriticalSectionChecker();
52 bool isBlockingFunction(
const CallEvent &Call)
const;
53 bool isLockFunction(
const CallEvent &Call)
const;
54 bool isUnlockFunction(
const CallEvent &Call)
const;
59 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
66 BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
67 : IILockGuard(nullptr), IIUniqueLock(nullptr),
68 LockFn(
"lock"), UnlockFn(
"unlock"), SleepFn(
"sleep"), GetcFn(
"getc"),
69 FgetsFn(
"fgets"), ReadFn(
"read"), RecvFn(
"recv"),
70 PthreadLockFn(
"pthread_mutex_lock"),
71 PthreadTryLockFn(
"pthread_mutex_trylock"),
72 PthreadUnlockFn(
"pthread_mutex_unlock"),
74 MtxTimedLock(
"mtx_timedlock"),
75 MtxTryLock(
"mtx_trylock"),
76 MtxUnlock(
"mtx_unlock"),
77 ClassLockGuard(
"lock_guard"),
78 ClassUniqueLock(
"unique_lock"),
79 IdentifierInfoInitialized(
false) {
81 BlockInCritSectionBugType.reset(
82 new BugType(
this,
"Call to blocking function in critical section",
86 void BlockInCriticalSectionChecker::initIdentifierInfo(
ASTContext &Ctx)
const {
87 if (!IdentifierInfoInitialized) {
93 IILockGuard = &Ctx.
Idents.
get(ClassLockGuard);
94 IIUniqueLock = &Ctx.
Idents.
get(ClassUniqueLock);
95 IdentifierInfoInitialized =
true;
99 bool BlockInCriticalSectionChecker::isBlockingFunction(
const CallEvent &Call)
const {
100 return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
103 bool BlockInCriticalSectionChecker::isLockFunction(
const CallEvent &Call)
const {
104 if (
const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
105 auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
110 return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
111 MtxTimedLock, MtxTryLock);
114 bool BlockInCriticalSectionChecker::isUnlockFunction(
const CallEvent &Call)
const {
115 if (
const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
116 const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
122 return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
125 void BlockInCriticalSectionChecker::checkPostCall(
const CallEvent &Call,
126 CheckerContext &
C)
const {
127 initIdentifierInfo(
C.getASTContext());
129 if (!isBlockingFunction(Call)
130 && !isLockFunction(Call)
131 && !isUnlockFunction(Call))
135 unsigned mutexCount =
State->get<MutexCounter>();
136 if (isUnlockFunction(Call) && mutexCount > 0) {
137 State =
State->set<MutexCounter>(--mutexCount);
139 }
else if (isLockFunction(Call)) {
140 State =
State->set<MutexCounter>(++mutexCount);
142 }
else if (mutexCount > 0) {
144 reportBlockInCritSection(BlockDesc, Call,
C);
148 void BlockInCriticalSectionChecker::reportBlockInCritSection(
149 SymbolRef BlockDescSym,
const CallEvent &Call, CheckerContext &
C)
const {
150 ExplodedNode *ErrNode =
C.generateNonFatalErrorNode();
155 llvm::raw_string_ostream os(msg);
156 os <<
"Call to blocking function '" <<
Call.getCalleeIdentifier()->getName()
157 <<
"' inside of critical section";
158 auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
160 R->addRange(
Call.getSourceRange());
161 R->markInteresting(BlockDescSym);
162 C.emitReport(std::move(R));
165 void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
166 mgr.registerChecker<BlockInCriticalSectionChecker>();
169 bool ento::shouldRegisterBlockInCriticalSectionChecker(
const CheckerManager &mgr) {