PthreadLockChecker.cpp revision 695fb502825a53ccd178ec1c85c77929d88acb71
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 22using namespace clang; 23using namespace ento; 24 25namespace { 26class PthreadLockChecker 27 : public CheckerVisitor<PthreadLockChecker> { 28 BugType *BT; 29public: 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. 47namespace { class LockSet {}; } 48namespace clang { 49namespace ento { 50template <> 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 57static void RegisterPthreadLockChecker(ExprEngine &Eng) { 58 Eng.registerCheck(new PthreadLockChecker()); 59} 60 61void ento::registerPthreadLockChecker(CheckerManager &mgr) { 62 mgr.addCheckerRegisterFunction(RegisterPthreadLockChecker); 63} 64 65 66void 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 98void 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 134void 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