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