PthreadLockChecker.cpp revision 5eca482fe895ea57bc82410222e6426c09e63284
1//===--- PthreadLockChecker.cpp - Check for locking problems ---*- 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.
11// Also handles XNU locks, which behave similarly enough to share code.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/StaticAnalyzer/Core/Checker.h"
17#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21#include "llvm/ADT/ImmutableList.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
28  mutable llvm::OwningPtr<BugType> BT_doublelock;
29  mutable llvm::OwningPtr<BugType> BT_lor;
30  enum LockingSemantics {
31    NotApplicable = 0,
32    PthreadSemantics,
33    XNUSemantics
34  };
35public:
36  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
37
38  void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
39                   bool isTryLock, enum LockingSemantics semantics) const;
40
41  void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
42};
43} // end anonymous namespace
44
45// GDM Entry for tracking lock state.
46namespace { class LockSet {}; }
47namespace clang {
48namespace ento {
49template <> struct ProgramStateTrait<LockSet> :
50  public ProgramStatePartialTrait<llvm::ImmutableList<const MemRegion*> > {
51    static void *GDMIndex() { static int x = 0; return &x; }
52};
53} // end of ento (ProgramState) namespace
54} // end clang namespace
55
56
57void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
58                                       CheckerContext &C) const {
59  const ProgramState *state = C.getState();
60  const LocationContext *LCtx = C.getLocationContext();
61  StringRef FName = C.getCalleeName(CE);
62  if (FName.empty())
63    return;
64
65  if (CE->getNumArgs() != 1)
66    return;
67
68  if (FName == "pthread_mutex_lock" ||
69      FName == "pthread_rwlock_rdlock" ||
70      FName == "pthread_rwlock_wrlock")
71    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
72                false, PthreadSemantics);
73  else if (FName == "lck_mtx_lock" ||
74           FName == "lck_rw_lock_exclusive" ||
75           FName == "lck_rw_lock_shared")
76    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
77                false, XNUSemantics);
78  else if (FName == "pthread_mutex_trylock" ||
79           FName == "pthread_rwlock_tryrdlock" ||
80           FName == "pthread_rwlock_tryrwlock")
81    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
82                true, PthreadSemantics);
83  else if (FName == "lck_mtx_try_lock" ||
84           FName == "lck_rw_try_lock_exclusive" ||
85           FName == "lck_rw_try_lock_shared")
86    AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
87                true, XNUSemantics);
88  else if (FName == "pthread_mutex_unlock" ||
89           FName == "pthread_rwlock_unlock" ||
90           FName == "lck_mtx_unlock" ||
91           FName == "lck_rw_done")
92    ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
93}
94
95void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
96                                     SVal lock, bool isTryLock,
97                                     enum LockingSemantics semantics) const {
98
99  const MemRegion *lockR = lock.getAsRegion();
100  if (!lockR)
101    return;
102
103  const ProgramState *state = C.getState();
104
105  SVal X = state->getSVal(CE, C.getLocationContext());
106  if (X.isUnknownOrUndef())
107    return;
108
109  DefinedSVal retVal = cast<DefinedSVal>(X);
110
111  if (state->contains<LockSet>(lockR)) {
112    if (!BT_doublelock)
113      BT_doublelock.reset(new BugType("Double locking", "Lock checker"));
114    ExplodedNode *N = C.generateSink();
115    if (!N)
116      return;
117    BugReport *report = new BugReport(*BT_doublelock,
118                                                      "This lock has already "
119                                                      "been acquired", N);
120    report->addRange(CE->getArg(0)->getSourceRange());
121    C.EmitReport(report);
122    return;
123  }
124
125  const ProgramState *lockSucc = state;
126  if (isTryLock) {
127    // Bifurcate the state, and allow a mode where the lock acquisition fails.
128    const ProgramState *lockFail;
129    switch (semantics) {
130    case PthreadSemantics:
131      llvm::tie(lockFail, lockSucc) = state->assume(retVal);
132      break;
133    case XNUSemantics:
134      llvm::tie(lockSucc, lockFail) = state->assume(retVal);
135      break;
136    default:
137      llvm_unreachable("Unknown tryLock locking semantics");
138      break;
139    }
140    assert(lockFail && lockSucc);
141    C.addTransition(lockFail);
142
143  } else if (semantics == PthreadSemantics) {
144    // Assume that the return value was 0.
145    lockSucc = state->assume(retVal, false);
146    assert(lockSucc);
147
148  } else {
149    // XNU locking semantics return void on non-try locks
150    assert((semantics == XNUSemantics) && "Unknown locking semantics");
151    lockSucc = state;
152  }
153
154  // Record that the lock was acquired.
155  lockSucc = lockSucc->add<LockSet>(lockR);
156  C.addTransition(lockSucc);
157}
158
159void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
160                                     SVal lock) const {
161
162  const MemRegion *lockR = lock.getAsRegion();
163  if (!lockR)
164    return;
165
166  const ProgramState *state = C.getState();
167  llvm::ImmutableList<const MemRegion*> LS = state->get<LockSet>();
168
169  // FIXME: Better analysis requires IPA for wrappers.
170  // FIXME: check for double unlocks
171  if (LS.isEmpty())
172    return;
173
174  const MemRegion *firstLockR = LS.getHead();
175  if (firstLockR != lockR) {
176    if (!BT_lor)
177      BT_lor.reset(new BugType("Lock order reversal", "Lock checker"));
178    ExplodedNode *N = C.generateSink();
179    if (!N)
180      return;
181    BugReport *report = new BugReport(*BT_lor,
182                                                      "This was not the most "
183                                                      "recently acquired lock. "
184                                                      "Possible lock order "
185                                                      "reversal", N);
186    report->addRange(CE->getArg(0)->getSourceRange());
187    C.EmitReport(report);
188    return;
189  }
190
191  // Record that the lock was released.
192  state = state->set<LockSet>(LS.getTail());
193  C.addTransition(state);
194}
195
196
197void ento::registerPthreadLockChecker(CheckerManager &mgr) {
198  mgr.registerChecker<PthreadLockChecker>();
199}
200