PthreadLockChecker.cpp revision af1a9330ffc0757e1534206f4f50eb420ef57b23
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 "ExperimentalChecks.h" 19#include "llvm/ADT/ImmutableSet.h" 20 21using namespace clang; 22using namespace ento; 23 24namespace { 25class PthreadLockChecker 26 : public CheckerVisitor<PthreadLockChecker> { 27 BugType *BT; 28public: 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. 46namespace { class LockSet {}; } 47namespace clang { 48namespace ento { 49template <> 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 56void ento::RegisterPthreadLockChecker(ExprEngine &Eng) { 57 Eng.registerCheck(new PthreadLockChecker()); 58} 59 60 61void 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 93void 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 129void 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