clang API Documentation
00001 //===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This defines PthreadLockChecker, a simple lock -> unlock checker. 00011 // Also handles XNU locks, which behave similarly enough to share code. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 00015 #include "ClangSACheckers.h" 00016 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00017 #include "clang/StaticAnalyzer/Core/Checker.h" 00018 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00019 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00020 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 00021 #include "llvm/ADT/ImmutableList.h" 00022 00023 using namespace clang; 00024 using namespace ento; 00025 00026 namespace { 00027 00028 struct LockState { 00029 enum Kind { Destroyed, Locked, Unlocked } K; 00030 00031 private: 00032 LockState(Kind K) : K(K) {} 00033 00034 public: 00035 static LockState getLocked(void) { return LockState(Locked); } 00036 static LockState getUnlocked(void) { return LockState(Unlocked); } 00037 static LockState getDestroyed(void) { return LockState(Destroyed); } 00038 00039 bool operator==(const LockState &X) const { 00040 return K == X.K; 00041 } 00042 00043 bool isLocked() const { return K == Locked; } 00044 bool isUnlocked() const { return K == Unlocked; } 00045 bool isDestroyed() const { return K == Destroyed; } 00046 00047 void Profile(llvm::FoldingSetNodeID &ID) const { 00048 ID.AddInteger(K); 00049 } 00050 }; 00051 00052 class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > { 00053 mutable std::unique_ptr<BugType> BT_doublelock; 00054 mutable std::unique_ptr<BugType> BT_doubleunlock; 00055 mutable std::unique_ptr<BugType> BT_destroylock; 00056 mutable std::unique_ptr<BugType> BT_initlock; 00057 mutable std::unique_ptr<BugType> BT_lor; 00058 enum LockingSemantics { 00059 NotApplicable = 0, 00060 PthreadSemantics, 00061 XNUSemantics 00062 }; 00063 public: 00064 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 00065 00066 void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, 00067 bool isTryLock, enum LockingSemantics semantics) const; 00068 00069 void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; 00070 void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; 00071 void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; 00072 void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const; 00073 }; 00074 } // end anonymous namespace 00075 00076 // GDM Entry for tracking lock state. 00077 REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) 00078 00079 REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) 00080 00081 void PthreadLockChecker::checkPostStmt(const CallExpr *CE, 00082 CheckerContext &C) const { 00083 ProgramStateRef state = C.getState(); 00084 const LocationContext *LCtx = C.getLocationContext(); 00085 StringRef FName = C.getCalleeName(CE); 00086 if (FName.empty()) 00087 return; 00088 00089 if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) 00090 return; 00091 00092 if (FName == "pthread_mutex_lock" || 00093 FName == "pthread_rwlock_rdlock" || 00094 FName == "pthread_rwlock_wrlock") 00095 AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), 00096 false, PthreadSemantics); 00097 else if (FName == "lck_mtx_lock" || 00098 FName == "lck_rw_lock_exclusive" || 00099 FName == "lck_rw_lock_shared") 00100 AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), 00101 false, XNUSemantics); 00102 else if (FName == "pthread_mutex_trylock" || 00103 FName == "pthread_rwlock_tryrdlock" || 00104 FName == "pthread_rwlock_trywrlock") 00105 AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), 00106 true, PthreadSemantics); 00107 else if (FName == "lck_mtx_try_lock" || 00108 FName == "lck_rw_try_lock_exclusive" || 00109 FName == "lck_rw_try_lock_shared") 00110 AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), 00111 true, XNUSemantics); 00112 else if (FName == "pthread_mutex_unlock" || 00113 FName == "pthread_rwlock_unlock" || 00114 FName == "lck_mtx_unlock" || 00115 FName == "lck_rw_done") 00116 ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); 00117 else if (FName == "pthread_mutex_destroy" || 00118 FName == "lck_mtx_destroy") 00119 DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); 00120 else if (FName == "pthread_mutex_init") 00121 InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); 00122 } 00123 00124 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, 00125 SVal lock, bool isTryLock, 00126 enum LockingSemantics semantics) const { 00127 00128 const MemRegion *lockR = lock.getAsRegion(); 00129 if (!lockR) 00130 return; 00131 00132 ProgramStateRef state = C.getState(); 00133 00134 SVal X = state->getSVal(CE, C.getLocationContext()); 00135 if (X.isUnknownOrUndef()) 00136 return; 00137 00138 DefinedSVal retVal = X.castAs<DefinedSVal>(); 00139 00140 if (const LockState *LState = state->get<LockMap>(lockR)) { 00141 if (LState->isLocked()) { 00142 if (!BT_doublelock) 00143 BT_doublelock.reset(new BugType(this, "Double locking", 00144 "Lock checker")); 00145 ExplodedNode *N = C.generateSink(); 00146 if (!N) 00147 return; 00148 BugReport *report = new BugReport(*BT_doublelock, 00149 "This lock has already been acquired", 00150 N); 00151 report->addRange(CE->getArg(0)->getSourceRange()); 00152 C.emitReport(report); 00153 return; 00154 } else if (LState->isDestroyed()) { 00155 reportUseDestroyedBug(C, CE); 00156 return; 00157 } 00158 } 00159 00160 ProgramStateRef lockSucc = state; 00161 if (isTryLock) { 00162 // Bifurcate the state, and allow a mode where the lock acquisition fails. 00163 ProgramStateRef lockFail; 00164 switch (semantics) { 00165 case PthreadSemantics: 00166 std::tie(lockFail, lockSucc) = state->assume(retVal); 00167 break; 00168 case XNUSemantics: 00169 std::tie(lockSucc, lockFail) = state->assume(retVal); 00170 break; 00171 default: 00172 llvm_unreachable("Unknown tryLock locking semantics"); 00173 } 00174 assert(lockFail && lockSucc); 00175 C.addTransition(lockFail); 00176 00177 } else if (semantics == PthreadSemantics) { 00178 // Assume that the return value was 0. 00179 lockSucc = state->assume(retVal, false); 00180 assert(lockSucc); 00181 00182 } else { 00183 // XNU locking semantics return void on non-try locks 00184 assert((semantics == XNUSemantics) && "Unknown locking semantics"); 00185 lockSucc = state; 00186 } 00187 00188 // Record that the lock was acquired. 00189 lockSucc = lockSucc->add<LockSet>(lockR); 00190 lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); 00191 C.addTransition(lockSucc); 00192 } 00193 00194 void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, 00195 SVal lock) const { 00196 00197 const MemRegion *lockR = lock.getAsRegion(); 00198 if (!lockR) 00199 return; 00200 00201 ProgramStateRef state = C.getState(); 00202 00203 if (const LockState *LState = state->get<LockMap>(lockR)) { 00204 if (LState->isUnlocked()) { 00205 if (!BT_doubleunlock) 00206 BT_doubleunlock.reset(new BugType(this, "Double unlocking", 00207 "Lock checker")); 00208 ExplodedNode *N = C.generateSink(); 00209 if (!N) 00210 return; 00211 BugReport *Report = new BugReport(*BT_doubleunlock, 00212 "This lock has already been unlocked", 00213 N); 00214 Report->addRange(CE->getArg(0)->getSourceRange()); 00215 C.emitReport(Report); 00216 return; 00217 } else if (LState->isDestroyed()) { 00218 reportUseDestroyedBug(C, CE); 00219 return; 00220 } 00221 } 00222 00223 LockSetTy LS = state->get<LockSet>(); 00224 00225 // FIXME: Better analysis requires IPA for wrappers. 00226 00227 if (!LS.isEmpty()) { 00228 const MemRegion *firstLockR = LS.getHead(); 00229 if (firstLockR != lockR) { 00230 if (!BT_lor) 00231 BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); 00232 ExplodedNode *N = C.generateSink(); 00233 if (!N) 00234 return; 00235 BugReport *report = new BugReport(*BT_lor, 00236 "This was not the most recently " 00237 "acquired lock. Possible lock order " 00238 "reversal", 00239 N); 00240 report->addRange(CE->getArg(0)->getSourceRange()); 00241 C.emitReport(report); 00242 return; 00243 } 00244 // Record that the lock was released. 00245 state = state->set<LockSet>(LS.getTail()); 00246 } 00247 00248 state = state->set<LockMap>(lockR, LockState::getUnlocked()); 00249 C.addTransition(state); 00250 } 00251 00252 void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, 00253 SVal Lock) const { 00254 00255 const MemRegion *LockR = Lock.getAsRegion(); 00256 if (!LockR) 00257 return; 00258 00259 ProgramStateRef State = C.getState(); 00260 00261 const LockState *LState = State->get<LockMap>(LockR); 00262 if (!LState || LState->isUnlocked()) { 00263 State = State->set<LockMap>(LockR, LockState::getDestroyed()); 00264 C.addTransition(State); 00265 return; 00266 } 00267 00268 StringRef Message; 00269 00270 if (LState->isLocked()) { 00271 Message = "This lock is still locked"; 00272 } else { 00273 Message = "This lock has already been destroyed"; 00274 } 00275 00276 if (!BT_destroylock) 00277 BT_destroylock.reset(new BugType(this, "Destroy invalid lock", 00278 "Lock checker")); 00279 ExplodedNode *N = C.generateSink(); 00280 if (!N) 00281 return; 00282 BugReport *Report = new BugReport(*BT_destroylock, Message, N); 00283 Report->addRange(CE->getArg(0)->getSourceRange()); 00284 C.emitReport(Report); 00285 } 00286 00287 void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, 00288 SVal Lock) const { 00289 00290 const MemRegion *LockR = Lock.getAsRegion(); 00291 if (!LockR) 00292 return; 00293 00294 ProgramStateRef State = C.getState(); 00295 00296 const struct LockState *LState = State->get<LockMap>(LockR); 00297 if (!LState || LState->isDestroyed()) { 00298 State = State->set<LockMap>(LockR, LockState::getUnlocked()); 00299 C.addTransition(State); 00300 return; 00301 } 00302 00303 StringRef Message; 00304 00305 if (LState->isLocked()) { 00306 Message = "This lock is still being held"; 00307 } else { 00308 Message = "This lock has already been initialized"; 00309 } 00310 00311 if (!BT_initlock) 00312 BT_initlock.reset(new BugType(this, "Init invalid lock", 00313 "Lock checker")); 00314 ExplodedNode *N = C.generateSink(); 00315 if (!N) 00316 return; 00317 BugReport *Report = new BugReport(*BT_initlock, Message, N); 00318 Report->addRange(CE->getArg(0)->getSourceRange()); 00319 C.emitReport(Report); 00320 } 00321 00322 void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, 00323 const CallExpr *CE) const { 00324 if (!BT_destroylock) 00325 BT_destroylock.reset(new BugType(this, "Use destroyed lock", 00326 "Lock checker")); 00327 ExplodedNode *N = C.generateSink(); 00328 if (!N) 00329 return; 00330 BugReport *Report = new BugReport(*BT_destroylock, 00331 "This lock has already been destroyed", 00332 N); 00333 Report->addRange(CE->getArg(0)->getSourceRange()); 00334 C.emitReport(Report); 00335 } 00336 00337 void ento::registerPthreadLockChecker(CheckerManager &mgr) { 00338 mgr.registerChecker<PthreadLockChecker>(); 00339 }