Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 1 | //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
Jordy Rose | a39e10f | 2011-07-19 20:31:42 +0000 | [diff] [blame] | 10 | // This defines PthreadLockChecker, a simple lock -> unlock checker. |
| 11 | // Also handles XNU locks, which behave similarly enough to share code. |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
Argyrios Kyrtzidis | 2d3905f | 2011-02-15 21:25:03 +0000 | [diff] [blame] | 15 | #include "ClangSACheckers.h" |
Chandler Carruth | 3a02247 | 2012-12-04 09:13:33 +0000 | [diff] [blame] | 16 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
Argyrios Kyrtzidis | 6a5674f | 2011-03-01 01:16:21 +0000 | [diff] [blame] | 17 | #include "clang/StaticAnalyzer/Core/Checker.h" |
Argyrios Kyrtzidis | 507ff53 | 2011-02-17 21:39:17 +0000 | [diff] [blame] | 18 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 19 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 20 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 21 | |
| 22 | using namespace clang; |
Ted Kremenek | 98857c9 | 2010-12-23 07:20:52 +0000 | [diff] [blame] | 23 | using namespace ento; |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 24 | |
| 25 | namespace { |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 26 | |
| 27 | struct LockState { |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 28 | enum Kind { |
| 29 | Destroyed, |
| 30 | Locked, |
| 31 | Unlocked, |
| 32 | UntouchedAndPossiblyDestroyed, |
| 33 | UnlockedAndPossiblyDestroyed |
| 34 | } K; |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 35 | |
| 36 | private: |
| 37 | LockState(Kind K) : K(K) {} |
| 38 | |
| 39 | public: |
Eugene Zelenko | d4304d2 | 2015-11-04 21:37:17 +0000 | [diff] [blame] | 40 | static LockState getLocked() { return LockState(Locked); } |
| 41 | static LockState getUnlocked() { return LockState(Unlocked); } |
| 42 | static LockState getDestroyed() { return LockState(Destroyed); } |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 43 | static LockState getUntouchedAndPossiblyDestroyed() { |
| 44 | return LockState(UntouchedAndPossiblyDestroyed); |
| 45 | } |
| 46 | static LockState getUnlockedAndPossiblyDestroyed() { |
| 47 | return LockState(UnlockedAndPossiblyDestroyed); |
| 48 | } |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 49 | |
| 50 | bool operator==(const LockState &X) const { |
| 51 | return K == X.K; |
| 52 | } |
| 53 | |
| 54 | bool isLocked() const { return K == Locked; } |
| 55 | bool isUnlocked() const { return K == Unlocked; } |
| 56 | bool isDestroyed() const { return K == Destroyed; } |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 57 | bool isUntouchedAndPossiblyDestroyed() const { |
| 58 | return K == UntouchedAndPossiblyDestroyed; |
| 59 | } |
| 60 | bool isUnlockedAndPossiblyDestroyed() const { |
| 61 | return K == UnlockedAndPossiblyDestroyed; |
| 62 | } |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 63 | |
| 64 | void Profile(llvm::FoldingSetNodeID &ID) const { |
| 65 | ID.AddInteger(K); |
| 66 | } |
| 67 | }; |
| 68 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 69 | class PthreadLockChecker |
| 70 | : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> { |
Ahmed Charles | b898432 | 2014-03-07 20:03:18 +0000 | [diff] [blame] | 71 | mutable std::unique_ptr<BugType> BT_doublelock; |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 72 | mutable std::unique_ptr<BugType> BT_doubleunlock; |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 73 | mutable std::unique_ptr<BugType> BT_destroylock; |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 74 | mutable std::unique_ptr<BugType> BT_initlock; |
Ahmed Charles | b898432 | 2014-03-07 20:03:18 +0000 | [diff] [blame] | 75 | mutable std::unique_ptr<BugType> BT_lor; |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 76 | enum LockingSemantics { |
| 77 | NotApplicable = 0, |
| 78 | PthreadSemantics, |
| 79 | XNUSemantics |
| 80 | }; |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 81 | public: |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 82 | void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 83 | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; |
Artem Dergachev | 8e32008 | 2017-10-10 11:49:09 +0000 | [diff] [blame^] | 84 | void printState(raw_ostream &Out, ProgramStateRef State, |
| 85 | const char *NL, const char *Sep) const override; |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 86 | |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 87 | void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, |
| 88 | bool isTryLock, enum LockingSemantics semantics) const; |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 89 | |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 90 | void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 91 | void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock, |
| 92 | enum LockingSemantics semantics) const; |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 93 | void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 94 | void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const; |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 95 | ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, |
| 96 | const MemRegion *lockR, |
| 97 | const SymbolRef *sym) const; |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 98 | }; |
| 99 | } // end anonymous namespace |
| 100 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 101 | // A stack of locks for tracking lock-unlock order. |
Jordan Rose | 0c153cb | 2012-11-02 01:54:06 +0000 | [diff] [blame] | 102 | REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 103 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 104 | // An entry for tracking lock states. |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 105 | REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 106 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 107 | // Return values for unresolved calls to pthread_mutex_destroy(). |
| 108 | REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef) |
| 109 | |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 110 | void PthreadLockChecker::checkPostStmt(const CallExpr *CE, |
| 111 | CheckerContext &C) const { |
Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 112 | ProgramStateRef state = C.getState(); |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 113 | const LocationContext *LCtx = C.getLocationContext(); |
Anna Zaks | c6aa531 | 2011-12-01 05:57:37 +0000 | [diff] [blame] | 114 | StringRef FName = C.getCalleeName(CE); |
| 115 | if (FName.empty()) |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 116 | return; |
Jordy Rose | a39e10f | 2011-07-19 20:31:42 +0000 | [diff] [blame] | 117 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 118 | if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 119 | return; |
Jordy Rose | a39e10f | 2011-07-19 20:31:42 +0000 | [diff] [blame] | 120 | |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 121 | if (FName == "pthread_mutex_lock" || |
| 122 | FName == "pthread_rwlock_rdlock" || |
| 123 | FName == "pthread_rwlock_wrlock") |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 124 | AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), |
| 125 | false, PthreadSemantics); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 126 | else if (FName == "lck_mtx_lock" || |
| 127 | FName == "lck_rw_lock_exclusive" || |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 128 | FName == "lck_rw_lock_shared") |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 129 | AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), |
| 130 | false, XNUSemantics); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 131 | else if (FName == "pthread_mutex_trylock" || |
| 132 | FName == "pthread_rwlock_tryrdlock" || |
Ted Kremenek | 115c3f7a | 2014-01-17 16:06:43 +0000 | [diff] [blame] | 133 | FName == "pthread_rwlock_trywrlock") |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 134 | AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), |
| 135 | true, PthreadSemantics); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 136 | else if (FName == "lck_mtx_try_lock" || |
| 137 | FName == "lck_rw_try_lock_exclusive" || |
| 138 | FName == "lck_rw_try_lock_shared") |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 139 | AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), |
| 140 | true, XNUSemantics); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 141 | else if (FName == "pthread_mutex_unlock" || |
| 142 | FName == "pthread_rwlock_unlock" || |
| 143 | FName == "lck_mtx_unlock" || |
| 144 | FName == "lck_rw_done") |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 145 | ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 146 | else if (FName == "pthread_mutex_destroy") |
| 147 | DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), PthreadSemantics); |
| 148 | else if (FName == "lck_mtx_destroy") |
| 149 | DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), XNUSemantics); |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 150 | else if (FName == "pthread_mutex_init") |
| 151 | InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 152 | } |
| 153 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 154 | // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not |
| 155 | // sure if the destroy call has succeeded or failed, and the lock enters one of |
| 156 | // the 'possibly destroyed' state. There is a short time frame for the |
| 157 | // programmer to check the return value to see if the lock was successfully |
| 158 | // destroyed. Before we model the next operation over that lock, we call this |
| 159 | // function to see if the return value was checked by now and set the lock state |
| 160 | // - either to destroyed state or back to its previous state. |
| 161 | |
| 162 | // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is |
| 163 | // successfully destroyed and it returns a non-zero value otherwise. |
| 164 | ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( |
| 165 | ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const { |
| 166 | const LockState *lstate = state->get<LockMap>(lockR); |
| 167 | // Existence in DestroyRetVal ensures existence in LockMap. |
| 168 | // Existence in Destroyed also ensures that the lock state for lockR is either |
| 169 | // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. |
| 170 | assert(lstate->isUntouchedAndPossiblyDestroyed() || |
| 171 | lstate->isUnlockedAndPossiblyDestroyed()); |
| 172 | |
| 173 | ConstraintManager &CMgr = state->getConstraintManager(); |
| 174 | ConditionTruthVal retZero = CMgr.isNull(state, *sym); |
| 175 | if (retZero.isConstrainedFalse()) { |
| 176 | if (lstate->isUntouchedAndPossiblyDestroyed()) |
| 177 | state = state->remove<LockMap>(lockR); |
| 178 | else if (lstate->isUnlockedAndPossiblyDestroyed()) |
| 179 | state = state->set<LockMap>(lockR, LockState::getUnlocked()); |
| 180 | } else |
| 181 | state = state->set<LockMap>(lockR, LockState::getDestroyed()); |
| 182 | |
| 183 | // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is |
| 184 | // now resolved. |
| 185 | state = state->remove<DestroyRetVal>(lockR); |
| 186 | return state; |
| 187 | } |
| 188 | |
Artem Dergachev | 8e32008 | 2017-10-10 11:49:09 +0000 | [diff] [blame^] | 189 | void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, |
| 190 | const char *NL, const char *Sep) const { |
| 191 | LockMapTy LM = State->get<LockMap>(); |
| 192 | if (!LM.isEmpty()) { |
| 193 | Out << Sep << "Mutex states:" << NL; |
| 194 | for (auto I : LM) { |
| 195 | I.first->dumpToStream(Out); |
| 196 | if (I.second.isLocked()) |
| 197 | Out << ": locked"; |
| 198 | else if (I.second.isUnlocked()) |
| 199 | Out << ": unlocked"; |
| 200 | else if (I.second.isDestroyed()) |
| 201 | Out << ": destroyed"; |
| 202 | else if (I.second.isUntouchedAndPossiblyDestroyed()) |
| 203 | Out << ": not tracked, possibly destroyed"; |
| 204 | else if (I.second.isUnlockedAndPossiblyDestroyed()) |
| 205 | Out << ": unlocked, possibly destroyed"; |
| 206 | Out << NL; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | LockSetTy LS = State->get<LockSet>(); |
| 211 | if (!LS.isEmpty()) { |
| 212 | Out << Sep << "Mutex lock order:" << NL; |
| 213 | for (auto I: LS) { |
| 214 | I->dumpToStream(Out); |
| 215 | Out << NL; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | // TODO: Dump destroyed mutex symbols? |
| 220 | } |
| 221 | |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 222 | void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 223 | SVal lock, bool isTryLock, |
| 224 | enum LockingSemantics semantics) const { |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 225 | |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 226 | const MemRegion *lockR = lock.getAsRegion(); |
| 227 | if (!lockR) |
| 228 | return; |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 229 | |
Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 230 | ProgramStateRef state = C.getState(); |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 231 | const SymbolRef *sym = state->get<DestroyRetVal>(lockR); |
| 232 | if (sym) |
| 233 | state = resolvePossiblyDestroyedMutex(state, lockR, sym); |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 234 | |
Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 235 | SVal X = state->getSVal(CE, C.getLocationContext()); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 236 | if (X.isUnknownOrUndef()) |
| 237 | return; |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 238 | |
David Blaikie | 2fdacbc | 2013-02-20 05:52:05 +0000 | [diff] [blame] | 239 | DefinedSVal retVal = X.castAs<DefinedSVal>(); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 240 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 241 | if (const LockState *LState = state->get<LockMap>(lockR)) { |
| 242 | if (LState->isLocked()) { |
| 243 | if (!BT_doublelock) |
| 244 | BT_doublelock.reset(new BugType(this, "Double locking", |
| 245 | "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 246 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 247 | if (!N) |
| 248 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 249 | auto report = llvm::make_unique<BugReport>( |
| 250 | *BT_doublelock, "This lock has already been acquired", N); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 251 | report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 252 | C.emitReport(std::move(report)); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 253 | return; |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 254 | } else if (LState->isDestroyed()) { |
| 255 | reportUseDestroyedBug(C, CE); |
| 256 | return; |
| 257 | } |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 258 | } |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 259 | |
Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 260 | ProgramStateRef lockSucc = state; |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 261 | if (isTryLock) { |
| 262 | // Bifurcate the state, and allow a mode where the lock acquisition fails. |
Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 263 | ProgramStateRef lockFail; |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 264 | switch (semantics) { |
| 265 | case PthreadSemantics: |
Benjamin Kramer | 867ea1d | 2014-03-02 13:01:17 +0000 | [diff] [blame] | 266 | std::tie(lockFail, lockSucc) = state->assume(retVal); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 267 | break; |
| 268 | case XNUSemantics: |
Benjamin Kramer | 867ea1d | 2014-03-02 13:01:17 +0000 | [diff] [blame] | 269 | std::tie(lockSucc, lockFail) = state->assume(retVal); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 270 | break; |
| 271 | default: |
| 272 | llvm_unreachable("Unknown tryLock locking semantics"); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 273 | } |
| 274 | assert(lockFail && lockSucc); |
Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 275 | C.addTransition(lockFail); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 276 | |
| 277 | } else if (semantics == PthreadSemantics) { |
| 278 | // Assume that the return value was 0. |
Ted Kremenek | c5bea1e | 2010-12-01 22:16:56 +0000 | [diff] [blame] | 279 | lockSucc = state->assume(retVal, false); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 280 | assert(lockSucc); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 281 | |
| 282 | } else { |
| 283 | // XNU locking semantics return void on non-try locks |
| 284 | assert((semantics == XNUSemantics) && "Unknown locking semantics"); |
| 285 | lockSucc = state; |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 286 | } |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 287 | |
| 288 | // Record that the lock was acquired. |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 289 | lockSucc = lockSucc->add<LockSet>(lockR); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 290 | lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); |
Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 291 | C.addTransition(lockSucc); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 295 | SVal lock) const { |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 296 | |
| 297 | const MemRegion *lockR = lock.getAsRegion(); |
| 298 | if (!lockR) |
| 299 | return; |
Ted Kremenek | 3a0678e | 2015-09-08 03:50:52 +0000 | [diff] [blame] | 300 | |
Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 301 | ProgramStateRef state = C.getState(); |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 302 | const SymbolRef *sym = state->get<DestroyRetVal>(lockR); |
| 303 | if (sym) |
| 304 | state = resolvePossiblyDestroyedMutex(state, lockR, sym); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 305 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 306 | if (const LockState *LState = state->get<LockMap>(lockR)) { |
| 307 | if (LState->isUnlocked()) { |
| 308 | if (!BT_doubleunlock) |
| 309 | BT_doubleunlock.reset(new BugType(this, "Double unlocking", |
| 310 | "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 311 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 312 | if (!N) |
| 313 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 314 | auto Report = llvm::make_unique<BugReport>( |
| 315 | *BT_doubleunlock, "This lock has already been unlocked", N); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 316 | Report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 317 | C.emitReport(std::move(Report)); |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 318 | return; |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 319 | } else if (LState->isDestroyed()) { |
| 320 | reportUseDestroyedBug(C, CE); |
| 321 | return; |
| 322 | } |
Jordy Rose | d9c5221 | 2011-07-19 20:21:41 +0000 | [diff] [blame] | 323 | } |
| 324 | |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 325 | LockSetTy LS = state->get<LockSet>(); |
| 326 | |
| 327 | // FIXME: Better analysis requires IPA for wrappers. |
| 328 | |
| 329 | if (!LS.isEmpty()) { |
| 330 | const MemRegion *firstLockR = LS.getHead(); |
| 331 | if (firstLockR != lockR) { |
| 332 | if (!BT_lor) |
| 333 | BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 334 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 335 | if (!N) |
| 336 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 337 | auto report = llvm::make_unique<BugReport>( |
| 338 | *BT_lor, "This was not the most recently acquired lock. Possible " |
| 339 | "lock order reversal", N); |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 340 | report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 341 | C.emitReport(std::move(report)); |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 342 | return; |
| 343 | } |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 344 | // Record that the lock was released. |
| 345 | state = state->set<LockSet>(LS.getTail()); |
Jordan Rose | 0696bb4 | 2014-04-01 03:40:38 +0000 | [diff] [blame] | 346 | } |
| 347 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 348 | state = state->set<LockMap>(lockR, LockState::getUnlocked()); |
Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 349 | C.addTransition(state); |
Ted Kremenek | d48568f | 2009-11-12 06:17:47 +0000 | [diff] [blame] | 350 | } |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 351 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 352 | void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 353 | SVal Lock, |
| 354 | enum LockingSemantics semantics) const { |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 355 | |
| 356 | const MemRegion *LockR = Lock.getAsRegion(); |
| 357 | if (!LockR) |
| 358 | return; |
| 359 | |
| 360 | ProgramStateRef State = C.getState(); |
| 361 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 362 | const SymbolRef *sym = State->get<DestroyRetVal>(LockR); |
| 363 | if (sym) |
| 364 | State = resolvePossiblyDestroyedMutex(State, LockR, sym); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 365 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 366 | const LockState *LState = State->get<LockMap>(LockR); |
| 367 | // Checking the return value of the destroy method only in the case of |
| 368 | // PthreadSemantics |
| 369 | if (semantics == PthreadSemantics) { |
| 370 | if (!LState || LState->isUnlocked()) { |
| 371 | SymbolRef sym = C.getSVal(CE).getAsSymbol(); |
| 372 | if (!sym) { |
| 373 | State = State->remove<LockMap>(LockR); |
| 374 | C.addTransition(State); |
| 375 | return; |
| 376 | } |
| 377 | State = State->set<DestroyRetVal>(LockR, sym); |
| 378 | if (LState && LState->isUnlocked()) |
| 379 | State = State->set<LockMap>( |
| 380 | LockR, LockState::getUnlockedAndPossiblyDestroyed()); |
| 381 | else |
| 382 | State = State->set<LockMap>( |
| 383 | LockR, LockState::getUntouchedAndPossiblyDestroyed()); |
| 384 | C.addTransition(State); |
| 385 | return; |
| 386 | } |
| 387 | } else { |
| 388 | if (!LState || LState->isUnlocked()) { |
| 389 | State = State->set<LockMap>(LockR, LockState::getDestroyed()); |
| 390 | C.addTransition(State); |
| 391 | return; |
| 392 | } |
| 393 | } |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 394 | StringRef Message; |
| 395 | |
| 396 | if (LState->isLocked()) { |
| 397 | Message = "This lock is still locked"; |
| 398 | } else { |
| 399 | Message = "This lock has already been destroyed"; |
| 400 | } |
| 401 | |
| 402 | if (!BT_destroylock) |
| 403 | BT_destroylock.reset(new BugType(this, "Destroy invalid lock", |
| 404 | "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 405 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 406 | if (!N) |
| 407 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 408 | auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 409 | Report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 410 | C.emitReport(std::move(Report)); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 411 | } |
| 412 | |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 413 | void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, |
| 414 | SVal Lock) const { |
| 415 | |
| 416 | const MemRegion *LockR = Lock.getAsRegion(); |
| 417 | if (!LockR) |
| 418 | return; |
| 419 | |
| 420 | ProgramStateRef State = C.getState(); |
| 421 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 422 | const SymbolRef *sym = State->get<DestroyRetVal>(LockR); |
| 423 | if (sym) |
| 424 | State = resolvePossiblyDestroyedMutex(State, LockR, sym); |
| 425 | |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 426 | const struct LockState *LState = State->get<LockMap>(LockR); |
| 427 | if (!LState || LState->isDestroyed()) { |
| 428 | State = State->set<LockMap>(LockR, LockState::getUnlocked()); |
| 429 | C.addTransition(State); |
| 430 | return; |
| 431 | } |
| 432 | |
| 433 | StringRef Message; |
| 434 | |
| 435 | if (LState->isLocked()) { |
| 436 | Message = "This lock is still being held"; |
| 437 | } else { |
| 438 | Message = "This lock has already been initialized"; |
| 439 | } |
| 440 | |
| 441 | if (!BT_initlock) |
| 442 | BT_initlock.reset(new BugType(this, "Init invalid lock", |
| 443 | "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 444 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 445 | if (!N) |
| 446 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 447 | auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N); |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 448 | Report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 449 | C.emitReport(std::move(Report)); |
Jordan Rose | 3a176ed | 2014-04-01 03:40:53 +0000 | [diff] [blame] | 450 | } |
| 451 | |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 452 | void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, |
| 453 | const CallExpr *CE) const { |
| 454 | if (!BT_destroylock) |
| 455 | BT_destroylock.reset(new BugType(this, "Use destroyed lock", |
| 456 | "Lock checker")); |
Devin Coughlin | e39bd40 | 2015-09-16 22:03:05 +0000 | [diff] [blame] | 457 | ExplodedNode *N = C.generateErrorNode(); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 458 | if (!N) |
| 459 | return; |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 460 | auto Report = llvm::make_unique<BugReport>( |
| 461 | *BT_destroylock, "This lock has already been destroyed", N); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 462 | Report->addRange(CE->getArg(0)->getSourceRange()); |
Aaron Ballman | 8d3a7a5 | 2015-06-23 13:15:32 +0000 | [diff] [blame] | 463 | C.emitReport(std::move(Report)); |
Jordan Rose | 7fcaa14 | 2014-04-01 03:40:47 +0000 | [diff] [blame] | 464 | } |
| 465 | |
Artem Dergachev | 7791593 | 2017-05-29 14:51:39 +0000 | [diff] [blame] | 466 | void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, |
| 467 | CheckerContext &C) const { |
| 468 | ProgramStateRef State = C.getState(); |
| 469 | |
| 470 | // TODO: Clean LockMap when a mutex region dies. |
| 471 | |
| 472 | DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>(); |
| 473 | for (DestroyRetValTy::iterator I = TrackedSymbols.begin(), |
| 474 | E = TrackedSymbols.end(); |
| 475 | I != E; ++I) { |
| 476 | const SymbolRef Sym = I->second; |
| 477 | const MemRegion *lockR = I->first; |
| 478 | bool IsSymDead = SymReaper.isDead(Sym); |
| 479 | // Remove the dead symbol from the return value symbols map. |
| 480 | if (IsSymDead) |
| 481 | State = resolvePossiblyDestroyedMutex(State, lockR, &Sym); |
| 482 | } |
| 483 | C.addTransition(State); |
| 484 | } |
| 485 | |
Argyrios Kyrtzidis | dff865d | 2011-02-23 01:05:36 +0000 | [diff] [blame] | 486 | void ento::registerPthreadLockChecker(CheckerManager &mgr) { |
| 487 | mgr.registerChecker<PthreadLockChecker>(); |
| 488 | } |