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