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