diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp new file mode 100644 index 000000000000..34c095f42e08 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -0,0 +1,152 @@ +//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with ExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class PthreadLockChecker + : public CheckerVisitor<PthreadLockChecker> { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class LockSet {}; } +namespace clang { +namespace ento { +template <> struct GRStateTrait<LockSet> : + public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end GR namespace +} // end clang namespace + +static void RegisterPthreadLockChecker(ExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + +void ento::registerPthreadLockChecker(CheckerManager &mgr) { + mgr.addCheckerRegisterFunction(RegisterPthreadLockChecker); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *R = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + IdentifierInfo *II = R->getDecl()->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return; + llvm::StringRef FName = II->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast<DefinedSVal>(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.generateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add<LockSet>(lockR); + + C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove<LockSet>(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.generateNode(CE, unlockState)); +} |