@@ -25,7 +25,13 @@ using namespace ento;
25
25
namespace {
26
26
27
27
struct LockState {
28
- enum Kind { Destroyed, Locked, Unlocked } K;
28
+ enum Kind {
29
+ Destroyed,
30
+ Locked,
31
+ Unlocked,
32
+ UntouchedAndPossiblyDestroyed,
33
+ UnlockedAndPossiblyDestroyed
34
+ } K;
29
35
30
36
private:
31
37
LockState (Kind K) : K(K) {}
@@ -34,6 +40,12 @@ struct LockState {
34
40
static LockState getLocked () { return LockState (Locked); }
35
41
static LockState getUnlocked () { return LockState (Unlocked); }
36
42
static LockState getDestroyed () { return LockState (Destroyed); }
43
+ static LockState getUntouchedAndPossiblyDestroyed () {
44
+ return LockState (UntouchedAndPossiblyDestroyed);
45
+ }
46
+ static LockState getUnlockedAndPossiblyDestroyed () {
47
+ return LockState (UnlockedAndPossiblyDestroyed);
48
+ }
37
49
38
50
bool operator ==(const LockState &X) const {
39
51
return K == X.K ;
@@ -42,13 +54,20 @@ struct LockState {
42
54
bool isLocked () const { return K == Locked; }
43
55
bool isUnlocked () const { return K == Unlocked; }
44
56
bool isDestroyed () const { return K == Destroyed; }
57
+ bool isUntouchedAndPossiblyDestroyed () const {
58
+ return K == UntouchedAndPossiblyDestroyed;
59
+ }
60
+ bool isUnlockedAndPossiblyDestroyed () const {
61
+ return K == UnlockedAndPossiblyDestroyed;
62
+ }
45
63
46
64
void Profile (llvm::FoldingSetNodeID &ID) const {
47
65
ID.AddInteger (K);
48
66
}
49
67
};
50
68
51
- class PthreadLockChecker : public Checker < check::PostStmt<CallExpr> > {
69
+ class PthreadLockChecker
70
+ : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
52
71
mutable std::unique_ptr<BugType> BT_doublelock;
53
72
mutable std::unique_ptr<BugType> BT_doubleunlock;
54
73
mutable std::unique_ptr<BugType> BT_destroylock;
@@ -61,22 +80,31 @@ class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
61
80
};
62
81
public:
63
82
void checkPostStmt (const CallExpr *CE, CheckerContext &C) const ;
83
+ void checkDeadSymbols (SymbolReaper &SymReaper, CheckerContext &C) const ;
64
84
65
85
void AcquireLock (CheckerContext &C, const CallExpr *CE, SVal lock,
66
86
bool isTryLock, enum LockingSemantics semantics) const ;
67
87
68
88
void ReleaseLock (CheckerContext &C, const CallExpr *CE, SVal lock) const ;
69
- void DestroyLock (CheckerContext &C, const CallExpr *CE, SVal Lock) const ;
89
+ void DestroyLock (CheckerContext &C, const CallExpr *CE, SVal Lock,
90
+ enum LockingSemantics semantics) const ;
70
91
void InitLock (CheckerContext &C, const CallExpr *CE, SVal Lock) const ;
71
92
void reportUseDestroyedBug (CheckerContext &C, const CallExpr *CE) const ;
93
+ ProgramStateRef resolvePossiblyDestroyedMutex (ProgramStateRef state,
94
+ const MemRegion *lockR,
95
+ const SymbolRef *sym) const ;
72
96
};
73
97
} // end anonymous namespace
74
98
75
- // GDM Entry for tracking lock state .
99
+ // A stack of locks for tracking lock-unlock order .
76
100
REGISTER_LIST_WITH_PROGRAMSTATE (LockSet, const MemRegion *)
77
101
102
+ // An entry for tracking lock states.
78
103
REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
79
104
105
+ // Return values for unresolved calls to pthread_mutex_destroy().
106
+ REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
107
+
80
108
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
81
109
CheckerContext &C) const {
82
110
ProgramStateRef state = C.getState ();
@@ -113,13 +141,49 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
113
141
FName == " lck_mtx_unlock" ||
114
142
FName == " lck_rw_done" )
115
143
ReleaseLock (C, CE, state->getSVal (CE->getArg (0 ), LCtx));
116
- else if (FName == " pthread_mutex_destroy" ||
117
- FName == " lck_mtx_destroy" )
118
- DestroyLock (C, CE, state->getSVal (CE->getArg (0 ), LCtx));
144
+ else if (FName == " pthread_mutex_destroy" )
145
+ DestroyLock (C, CE, state->getSVal (CE->getArg (0 ), LCtx), PthreadSemantics);
146
+ else if (FName == " lck_mtx_destroy" )
147
+ DestroyLock (C, CE, state->getSVal (CE->getArg (0 ), LCtx), XNUSemantics);
119
148
else if (FName == " pthread_mutex_init" )
120
149
InitLock (C, CE, state->getSVal (CE->getArg (0 ), LCtx));
121
150
}
122
151
152
+ // When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
153
+ // sure if the destroy call has succeeded or failed, and the lock enters one of
154
+ // the 'possibly destroyed' state. There is a short time frame for the
155
+ // programmer to check the return value to see if the lock was successfully
156
+ // destroyed. Before we model the next operation over that lock, we call this
157
+ // function to see if the return value was checked by now and set the lock state
158
+ // - either to destroyed state or back to its previous state.
159
+
160
+ // In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
161
+ // successfully destroyed and it returns a non-zero value otherwise.
162
+ ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex (
163
+ ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
164
+ const LockState *lstate = state->get <LockMap>(lockR);
165
+ // Existence in DestroyRetVal ensures existence in LockMap.
166
+ // Existence in Destroyed also ensures that the lock state for lockR is either
167
+ // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
168
+ assert (lstate->isUntouchedAndPossiblyDestroyed () ||
169
+ lstate->isUnlockedAndPossiblyDestroyed ());
170
+
171
+ ConstraintManager &CMgr = state->getConstraintManager ();
172
+ ConditionTruthVal retZero = CMgr.isNull (state, *sym);
173
+ if (retZero.isConstrainedFalse ()) {
174
+ if (lstate->isUntouchedAndPossiblyDestroyed ())
175
+ state = state->remove <LockMap>(lockR);
176
+ else if (lstate->isUnlockedAndPossiblyDestroyed ())
177
+ state = state->set <LockMap>(lockR, LockState::getUnlocked ());
178
+ } else
179
+ state = state->set <LockMap>(lockR, LockState::getDestroyed ());
180
+
181
+ // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
182
+ // now resolved.
183
+ state = state->remove <DestroyRetVal>(lockR);
184
+ return state;
185
+ }
186
+
123
187
void PthreadLockChecker::AcquireLock (CheckerContext &C, const CallExpr *CE,
124
188
SVal lock, bool isTryLock,
125
189
enum LockingSemantics semantics) const {
@@ -129,6 +193,9 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
129
193
return ;
130
194
131
195
ProgramStateRef state = C.getState ();
196
+ const SymbolRef *sym = state->get <DestroyRetVal>(lockR);
197
+ if (sym)
198
+ state = resolvePossiblyDestroyedMutex (state, lockR, sym);
132
199
133
200
SVal X = state->getSVal (CE, C.getLocationContext ());
134
201
if (X.isUnknownOrUndef ())
@@ -197,6 +264,9 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
197
264
return ;
198
265
199
266
ProgramStateRef state = C.getState ();
267
+ const SymbolRef *sym = state->get <DestroyRetVal>(lockR);
268
+ if (sym)
269
+ state = resolvePossiblyDestroyedMutex (state, lockR, sym);
200
270
201
271
if (const LockState *LState = state->get <LockMap>(lockR)) {
202
272
if (LState->isUnlocked ()) {
@@ -245,21 +315,47 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
245
315
}
246
316
247
317
void PthreadLockChecker::DestroyLock (CheckerContext &C, const CallExpr *CE,
248
- SVal Lock) const {
318
+ SVal Lock,
319
+ enum LockingSemantics semantics) const {
249
320
250
321
const MemRegion *LockR = Lock.getAsRegion ();
251
322
if (!LockR)
252
323
return ;
253
324
254
325
ProgramStateRef State = C.getState ();
255
326
327
+ const SymbolRef *sym = State->get <DestroyRetVal>(LockR);
328
+ if (sym)
329
+ State = resolvePossiblyDestroyedMutex (State, LockR, sym);
330
+
256
331
const LockState *LState = State->get <LockMap>(LockR);
257
- if (!LState || LState->isUnlocked ()) {
258
- State = State->set <LockMap>(LockR, LockState::getDestroyed ());
259
- C.addTransition (State);
260
- return ;
332
+ // Checking the return value of the destroy method only in the case of
333
+ // PthreadSemantics
334
+ if (semantics == PthreadSemantics) {
335
+ if (!LState || LState->isUnlocked ()) {
336
+ SymbolRef sym = C.getSVal (CE).getAsSymbol ();
337
+ if (!sym) {
338
+ State = State->remove <LockMap>(LockR);
339
+ C.addTransition (State);
340
+ return ;
341
+ }
342
+ State = State->set <DestroyRetVal>(LockR, sym);
343
+ if (LState && LState->isUnlocked ())
344
+ State = State->set <LockMap>(
345
+ LockR, LockState::getUnlockedAndPossiblyDestroyed ());
346
+ else
347
+ State = State->set <LockMap>(
348
+ LockR, LockState::getUntouchedAndPossiblyDestroyed ());
349
+ C.addTransition (State);
350
+ return ;
351
+ }
352
+ } else {
353
+ if (!LState || LState->isUnlocked ()) {
354
+ State = State->set <LockMap>(LockR, LockState::getDestroyed ());
355
+ C.addTransition (State);
356
+ return ;
357
+ }
261
358
}
262
-
263
359
StringRef Message;
264
360
265
361
if (LState->isLocked ()) {
@@ -288,6 +384,10 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
288
384
289
385
ProgramStateRef State = C.getState ();
290
386
387
+ const SymbolRef *sym = State->get <DestroyRetVal>(LockR);
388
+ if (sym)
389
+ State = resolvePossiblyDestroyedMutex (State, LockR, sym);
390
+
291
391
const struct LockState *LState = State->get <LockMap>(LockR);
292
392
if (!LState || LState->isDestroyed ()) {
293
393
State = State->set <LockMap>(LockR, LockState::getUnlocked ());
@@ -328,6 +428,26 @@ void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
328
428
C.emitReport (std::move (Report));
329
429
}
330
430
431
+ void PthreadLockChecker::checkDeadSymbols (SymbolReaper &SymReaper,
432
+ CheckerContext &C) const {
433
+ ProgramStateRef State = C.getState ();
434
+
435
+ // TODO: Clean LockMap when a mutex region dies.
436
+
437
+ DestroyRetValTy TrackedSymbols = State->get <DestroyRetVal>();
438
+ for (DestroyRetValTy::iterator I = TrackedSymbols.begin (),
439
+ E = TrackedSymbols.end ();
440
+ I != E; ++I) {
441
+ const SymbolRef Sym = I->second ;
442
+ const MemRegion *lockR = I->first ;
443
+ bool IsSymDead = SymReaper.isDead (Sym);
444
+ // Remove the dead symbol from the return value symbols map.
445
+ if (IsSymDead)
446
+ State = resolvePossiblyDestroyedMutex (State, lockR, &Sym);
447
+ }
448
+ C.addTransition (State);
449
+ }
450
+
331
451
void ento::registerPthreadLockChecker (CheckerManager &mgr) {
332
452
mgr.registerChecker <PthreadLockChecker>();
333
453
}
0 commit comments