PthreadLockChecker.cpp revision 983326f32c746f5e47161a73758e4d363263dd2c
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 23using namespace clang; 24using namespace ento; 25 26namespace { 27class PthreadLockChecker 28 : public CheckerV2< check::PostStmt<CallExpr> > { 29public: 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. 42namespace { class LockSet {}; } 43namespace clang { 44namespace ento { 45template <> 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 53void 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 85void 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 121void 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 141void ento::registerPthreadLockChecker(CheckerManager &mgr) { 142 mgr.registerChecker<PthreadLockChecker>(); 143} 144