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