1 //===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// 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 // 10 // This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually 11 // this shouldn't be registered with ExprEngineInternalChecks. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "ClangSACheckers.h" 16 #include "clang/StaticAnalyzer/Core/CheckerV2.h" 17 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" 21 #include "llvm/ADT/ImmutableSet.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 class PthreadLockChecker 28 : public CheckerV2< check::PostStmt<CallExpr> > { 29 public: 30 void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; 31 32 void AcquireLock(CheckerContext &C, const CallExpr *CE, 33 SVal lock, bool isTryLock) const; 34 35 void ReleaseLock(CheckerContext &C, const CallExpr *CE, 36 SVal lock) const; 37 38 }; 39 } // end anonymous namespace 40 41 // GDM Entry for tracking lock state. 42 namespace { class LockSet {}; } 43 namespace clang { 44 namespace ento { 45 template <> struct GRStateTrait<LockSet> : 46 public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { 47 static void* GDMIndex() { static int x = 0; return &x; } 48 }; 49 } // end GR namespace 50 } // end clang namespace 51 52 53 void PthreadLockChecker::checkPostStmt(const CallExpr *CE, 54 CheckerContext &C) const { 55 const GRState *state = C.getState(); 56 const Expr *Callee = CE->getCallee(); 57 const FunctionTextRegion *R = 58 dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); 59 60 if (!R) 61 return; 62 63 IdentifierInfo *II = R->getDecl()->getIdentifier(); 64 if (!II) // if no identifier, not a simple C function 65 return; 66 llvm::StringRef FName = II->getName(); 67 68 if (FName == "pthread_mutex_lock") { 69 if (CE->getNumArgs() != 1) 70 return; 71 AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); 72 } 73 else if (FName == "pthread_mutex_trylock") { 74 if (CE->getNumArgs() != 1) 75 return; 76 AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); 77 } 78 else if (FName == "pthread_mutex_unlock") { 79 if (CE->getNumArgs() != 1) 80 return; 81 ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); 82 } 83 } 84 85 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, 86 SVal lock, bool isTryLock) const { 87 88 const MemRegion *lockR = lock.getAsRegion(); 89 if (!lockR) 90 return; 91 92 const GRState *state = C.getState(); 93 94 SVal X = state->getSVal(CE); 95 if (X.isUnknownOrUndef()) 96 return; 97 98 DefinedSVal retVal = cast<DefinedSVal>(X); 99 const GRState *lockSucc = state; 100 101 if (isTryLock) { 102 // Bifurcate the state, and allow a mode where the lock acquisition fails. 103 const GRState *lockFail; 104 llvm::tie(lockFail, lockSucc) = state->assume(retVal); 105 assert(lockFail && lockSucc); 106 C.addTransition(C.generateNode(CE, lockFail)); 107 } 108 else { 109 // Assume that the return value was 0. 110 lockSucc = state->assume(retVal, false); 111 assert(lockSucc); 112 } 113 114 // Record that the lock was acquired. 115 lockSucc = lockSucc->add<LockSet>(lockR); 116 117 C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : 118 C.getPredecessor()); 119 } 120 121 void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, 122 SVal lock) const { 123 124 const MemRegion *lockR = lock.getAsRegion(); 125 if (!lockR) 126 return; 127 128 const GRState *state = C.getState(); 129 130 // Record that the lock was released. 131 // FIXME: Handle unlocking locks that were never acquired. This may 132 // require IPA for wrappers. 133 const GRState *unlockState = state->remove<LockSet>(lockR); 134 135 if (state == unlockState) 136 return; 137 138 C.addTransition(C.generateNode(CE, unlockState)); 139 } 140 141 void ento::registerPthreadLockChecker(CheckerManager &mgr) { 142 mgr.registerChecker<PthreadLockChecker>(); 143 } 144