| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| 18 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| 19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
| 20 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| 21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| 22 | |
| 23 | using namespace clang; |
| 24 | using namespace ento; |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | class BlockInCriticalSectionChecker : public Checker<check::PostCall> { |
| 29 | |
| 30 | mutable IdentifierInfo *IILockGuard, *IIUniqueLock; |
| 31 | |
| 32 | CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, |
| 33 | PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, |
| 34 | MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; |
| 35 | |
| 36 | StringRef ClassLockGuard, ClassUniqueLock; |
| 37 | |
| 38 | mutable bool IdentifierInfoInitialized; |
| 39 | |
| 40 | std::unique_ptr<BugType> BlockInCritSectionBugType; |
| 41 | |
| 42 | void initIdentifierInfo(ASTContext &Ctx) const; |
| 43 | |
| 44 | void reportBlockInCritSection(SymbolRef FileDescSym, |
| 45 | const CallEvent &call, |
| 46 | CheckerContext &C) const; |
| 47 | |
| 48 | public: |
| 49 | BlockInCriticalSectionChecker(); |
| 50 | |
| 51 | bool isBlockingFunction(const CallEvent &Call) const; |
| 52 | bool isLockFunction(const CallEvent &Call) const; |
| 53 | bool isUnlockFunction(const CallEvent &Call) const; |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
| 59 | }; |
| 60 | |
| 61 | } |
| 62 | |
| 63 | REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) |
| 64 | |
| 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"), |
| 72 | MtxLock("mtx_lock"), |
| 73 | MtxTimedLock("mtx_timedlock"), |
| 74 | MtxTryLock("mtx_trylock"), |
| 75 | MtxUnlock("mtx_unlock"), |
| 76 | ClassLockGuard("lock_guard"), |
| 77 | ClassUniqueLock("unique_lock"), |
| 78 | IdentifierInfoInitialized(false) { |
| 79 | |
| 80 | BlockInCritSectionBugType.reset( |
| 81 | new BugType(this, "Call to blocking function in critical section", |
| 82 | "Blocking Error")); |
| 83 | } |
| 84 | |
| 85 | void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { |
| 86 | if (!IdentifierInfoInitialized) { |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | IILockGuard = &Ctx.Idents.get(ClassLockGuard); |
| 93 | IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock); |
| 94 | IdentifierInfoInitialized = true; |
| 95 | } |
| 96 | } |
| 97 | |
| 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)) { |
| 104 | return true; |
| 105 | } |
| 106 | return false; |
| 107 | } |
| 108 | |
| 109 | bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { |
| 110 | if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { |
| 111 | auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier(); |
| 112 | if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) |
| 113 | return true; |
| 114 | } |
| 115 | |
| 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)) { |
| 122 | return true; |
| 123 | } |
| 124 | return false; |
| 125 | } |
| 126 | |
| 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()); |
| 130 | auto IdentifierInfo = DRecordDecl->getIdentifier(); |
| 131 | if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) |
| 132 | return true; |
| 133 | } |
| 134 | |
| 135 | if (Call.isCalled(UnlockFn) |
| 136 | || Call.isCalled(PthreadUnlockFn) |
| 137 | || Call.isCalled(MtxUnlock)) { |
| 138 | return true; |
| 139 | } |
| 140 | return false; |
| 141 | } |
| 142 | |
| 143 | void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, |
| 144 | CheckerContext &C) const { |
| 145 | initIdentifierInfo(C.getASTContext()); |
| 146 | |
| 147 | if (!isBlockingFunction(Call) |
| 148 | && !isLockFunction(Call) |
| 149 | && !isUnlockFunction(Call)) |
| 150 | return; |
| 151 | |
| 152 | ProgramStateRef State = C.getState(); |
| 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); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | void BlockInCriticalSectionChecker::reportBlockInCritSection( |
| 167 | SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { |
| 168 | ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); |
| 169 | if (!ErrNode) |
| 170 | return; |
| 171 | |
| 172 | std::string msg; |
| 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)); |
| 180 | } |
| 181 | |
| 182 | void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { |
| 183 | mgr.registerChecker<BlockInCriticalSectionChecker>(); |
| 184 | } |
| 185 | |
| 186 | bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) { |
| 187 | return true; |
| 188 | } |
| 189 | |