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