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