blob: 09632547c881820357d099a0d69948c8661ca622 [file] [log] [blame]
Ted Kremenekd48568f2009-11-12 06:17:47 +00001//===--- 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 GRExprEngineInternalChecks.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
16#include "clang/Analysis/PathSensitive/BugReporter.h"
17#include "clang/Analysis/PathSensitive/GRStateTrait.h"
18#include "GRExprEngineExperimentalChecks.h"
19#include "llvm/ADT/ImmutableSet.h"
20
21using namespace clang;
22
23namespace {
24class VISIBILITY_HIDDEN PthreadLockChecker
25 : public CheckerVisitor<PthreadLockChecker> {
26 BugType *BT;
27public:
28 PthreadLockChecker() : BT(0) {}
29 static void *getTag() {
30 static int x = 0;
31 return &x;
32 }
33 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
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 VISIBILITY_HIDDEN LockSet {}; }
47namespace clang {
48template <> struct GRStateTrait<LockSet> :
49 public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
50 static void* GDMIndex() { return PthreadLockChecker::getTag(); }
51};
52} // end clang namespace
53
54void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) {
55 Eng.registerCheck(new PthreadLockChecker());
56}
57
58void PthreadLockChecker::PreVisitCallExpr(CheckerContext &C,
59 const CallExpr *CE) {
60 const GRState *state = C.getState();
61 const Expr *Callee = CE->getCallee();
62 const CodeTextRegion *R =
63 dyn_cast_or_null<CodeTextRegion>(state->getSVal(Callee).getAsRegion());
64
65 if (!R)
66 return;
67
68 llvm::StringRef FName = R->getDecl()->getName();
69
70 if (FName == "pthread_mutex_lock") {
71 if (CE->getNumArgs() != 1)
72 return;
73 AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false);
74 }
75 else if (FName == "pthread_mutex_trylock") {
76 if (CE->getNumArgs() != 1)
77 return;
78 AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true);
79 }
80}
81
82void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C,
83 const CallExpr *CE) {
84 const GRState *state = C.getState();
85 const Expr *Callee = CE->getCallee();
86 const CodeTextRegion *R =
87 dyn_cast_or_null<CodeTextRegion>(state->getSVal(Callee).getAsRegion());
88
89 if (!R)
90 return;
91
92 llvm::StringRef FName = R->getDecl()->getName();
93
94 if (FName == "pthread_mutex_unlock") {
95 if (CE->getNumArgs() != 1)
96 return;
97 ReleaseLock(C, CE, state->getSVal(CE->getArg(0)));
98 }
99}
100
101void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
102 SVal lock, bool isTryLock) {
103
104 const MemRegion *lockR = lock.getAsRegion();
105 if (!lockR)
106 return;
107
108 const GRState *state = C.getState();
109
110 SVal X = state->getSVal(CE);
111 if (X.isUnknownOrUndef())
112 return;
113
114 DefinedSVal retVal = cast<DefinedSVal>(X);
115 const GRState *lockSucc = state;
116
117 if (isTryLock) {
118 // Bifurcate the state, and allow a mode where the lock acquisition fails.
119 const GRState *lockFail;
120 llvm::tie(lockFail, lockSucc) = state->Assume(retVal);
121 assert(lockFail && lockSucc);
122 C.addTransition(C.GenerateNode(CE, lockFail));
123 }
124 else {
125 // Assume that the return value was 0.
126 lockSucc = state->Assume(retVal, false);
127 assert(lockSucc);
128 }
129
130 // Record that the lock was acquired.
131 lockSucc = lockSucc->add<LockSet>(lockR);
132
133 C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) :
134 C.getPredecessor());
135}
136
137void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
138 SVal lock) {
139
140 const MemRegion *lockR = lock.getAsRegion();
141 if (!lockR)
142 return;
143
144 const GRState *state = C.getState();
145
146 // Record that the lock was released.
147 // FIXME: Handle unlocking locks that were never acquired. This may
148 // require IPA for wrappers.
149 const GRState *unlockState = state->remove<LockSet>(lockR);
150
151 if (state == unlockState)
152 return;
153
154 C.addTransition(C.GenerateNode(CE, unlockState));
155}