15
15
#include " SmartPtr.h"
16
16
17
17
#include " clang/AST/DeclCXX.h"
18
+ #include " clang/AST/DeclarationName.h"
18
19
#include " clang/AST/ExprCXX.h"
19
20
#include " clang/AST/Type.h"
20
21
#include " clang/Basic/LLVM.h"
@@ -35,9 +36,10 @@ using namespace ento;
35
36
36
37
namespace {
37
38
class SmartPtrModeling
38
- : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges> {
39
+ : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40
+ check::LiveSymbols> {
39
41
40
- bool isAssignOpMethod (const CallEvent &Call) const ;
42
+ bool isBoolConversionMethod (const CallEvent &Call) const ;
41
43
42
44
public:
43
45
// Whether the checker should model for null dereferences of smart pointers.
@@ -51,6 +53,9 @@ class SmartPtrModeling
51
53
ArrayRef<const MemRegion *> ExplicitRegions,
52
54
ArrayRef<const MemRegion *> Regions,
53
55
const LocationContext *LCtx, const CallEvent *Call) const ;
56
+ void printState (raw_ostream &Out, ProgramStateRef State, const char *NL,
57
+ const char *Sep) const ;
58
+ void checkLiveSymbols (ProgramStateRef State, SymbolReaper &SR) const ;
54
59
55
60
private:
56
61
void handleReset (const CallEvent &Call, CheckerContext &C) const ;
@@ -62,6 +67,7 @@ class SmartPtrModeling
62
67
const MemRegion *ThisRegion) const ;
63
68
bool updateMovedSmartPointers (CheckerContext &C, const MemRegion *ThisRegion,
64
69
const MemRegion *OtherSmartPtrRegion) const ;
70
+ void handleBoolConversion (const CallEvent &Call, CheckerContext &C) const ;
65
71
66
72
using SmartPtrMethodHandlerFn =
67
73
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const ;
@@ -128,7 +134,38 @@ static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
128
134
return State;
129
135
}
130
136
131
- bool SmartPtrModeling::isAssignOpMethod (const CallEvent &Call) const {
137
+ // Helper method to get the inner pointer type of specialized smart pointer
138
+ // Returns empty type if not found valid inner pointer type.
139
+ static QualType getInnerPointerType (const CallEvent &Call, CheckerContext &C) {
140
+ const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl ());
141
+ if (!MethodDecl || !MethodDecl->getParent ())
142
+ return {};
143
+
144
+ const auto *RecordDecl = MethodDecl->getParent ();
145
+ if (!RecordDecl || !RecordDecl->isInStdNamespace ())
146
+ return {};
147
+
148
+ const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
149
+ if (!TSD)
150
+ return {};
151
+
152
+ auto TemplateArgs = TSD->getTemplateArgs ().asArray ();
153
+ if (TemplateArgs.size () == 0 )
154
+ return {};
155
+ auto InnerValueType = TemplateArgs[0 ].getAsType ();
156
+ return C.getASTContext ().getPointerType (InnerValueType.getCanonicalType ());
157
+ }
158
+
159
+ // Helper method to pretty print region and avoid extra spacing.
160
+ static void checkAndPrettyPrintRegion (llvm::raw_ostream &OS,
161
+ const MemRegion *Region) {
162
+ if (Region->canPrintPretty ()) {
163
+ OS << " " ;
164
+ Region->printPretty (OS);
165
+ }
166
+ }
167
+
168
+ bool SmartPtrModeling::isBoolConversionMethod (const CallEvent &Call) const {
132
169
// TODO: Update CallDescription to support anonymous calls?
133
170
// TODO: Handle other methods, such as .get() or .release().
134
171
// But once we do, we'd need a visitor to explain null dereferences
@@ -143,21 +180,31 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
143
180
if (!smartptr::isStdSmartPtrCall (Call))
144
181
return false ;
145
182
146
- if (isAssignOpMethod (Call)) {
183
+ if (isBoolConversionMethod (Call)) {
147
184
const MemRegion *ThisR =
148
185
cast<CXXInstanceCall>(&Call)->getCXXThisVal ().getAsRegion ();
149
186
150
- if (!move::isMovedFrom (State, ThisR)) {
151
- // TODO: Model this case as well. At least, avoid invalidation of
152
- // globals.
153
- return false ;
187
+ if (ModelSmartPtrDereference) {
188
+ // The check for the region is moved is duplicated in handleBoolOperation
189
+ // method.
190
+ // FIXME: Once we model std::move for smart pointers clean up this and use
191
+ // that modeling.
192
+ handleBoolConversion (Call, C);
193
+ return true ;
194
+ } else {
195
+ if (!move::isMovedFrom (State, ThisR)) {
196
+ // TODO: Model this case as well. At least, avoid invalidation of
197
+ // globals.
198
+ return false ;
199
+ }
200
+
201
+ // TODO: Add a note to bug reports describing this decision.
202
+ C.addTransition (State->BindExpr (
203
+ Call.getOriginExpr (), C.getLocationContext (),
204
+ C.getSValBuilder ().makeZeroVal (Call.getResultType ())));
205
+
206
+ return true ;
154
207
}
155
-
156
- // TODO: Add a note to bug reports describing this decision.
157
- C.addTransition (
158
- State->BindExpr (Call.getOriginExpr (), C.getLocationContext (),
159
- C.getSValBuilder ().makeZeroVal (Call.getResultType ())));
160
- return true ;
161
208
}
162
209
163
210
if (!ModelSmartPtrDereference)
@@ -184,8 +231,8 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
184
231
if (&BR.getBugType () != smartptr::getNullDereferenceBugType () ||
185
232
!BR.isInteresting (ThisRegion))
186
233
return ;
187
- OS << " Default constructed smart pointer " ;
188
- ThisRegion-> printPretty (OS);
234
+ OS << " Default constructed smart pointer" ;
235
+ checkAndPrettyPrintRegion (OS, ThisRegion );
189
236
OS << " is null" ;
190
237
}));
191
238
} else {
@@ -202,8 +249,8 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
202
249
!BR.isInteresting (ThisRegion))
203
250
return ;
204
251
bugreporter::trackExpressionValue (BR.getErrorNode (), TrackingExpr, BR);
205
- OS << " Smart pointer " ;
206
- ThisRegion-> printPretty (OS);
252
+ OS << " Smart pointer" ;
253
+ checkAndPrettyPrintRegion (OS, ThisRegion );
207
254
if (ArgVal.isZeroConstant ())
208
255
OS << " is constructed using a null value" ;
209
256
else
@@ -239,6 +286,23 @@ void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
239
286
C.addTransition (State);
240
287
}
241
288
289
+ void SmartPtrModeling::printState (raw_ostream &Out, ProgramStateRef State,
290
+ const char *NL, const char *Sep) const {
291
+ TrackedRegionMapTy RS = State->get <TrackedRegionMap>();
292
+
293
+ if (!RS.isEmpty ()) {
294
+ Out << Sep << " Smart ptr regions :" << NL;
295
+ for (auto I : RS) {
296
+ I.first ->dumpToStream (Out);
297
+ if (smartptr::isNullSmartPtr (State, I.first ))
298
+ Out << " : Null" ;
299
+ else
300
+ Out << " : Non Null" ;
301
+ Out << NL;
302
+ }
303
+ }
304
+ }
305
+
242
306
ProgramStateRef SmartPtrModeling::checkRegionChanges (
243
307
ProgramStateRef State, const InvalidatedSymbols *Invalidated,
244
308
ArrayRef<const MemRegion *> ExplicitRegions,
@@ -253,6 +317,18 @@ ProgramStateRef SmartPtrModeling::checkRegionChanges(
253
317
return State->set <TrackedRegionMap>(RegionMap);
254
318
}
255
319
320
+ void SmartPtrModeling::checkLiveSymbols (ProgramStateRef State,
321
+ SymbolReaper &SR) const {
322
+ // Marking tracked symbols alive
323
+ TrackedRegionMapTy TrackedRegions = State->get <TrackedRegionMap>();
324
+ for (auto I = TrackedRegions.begin (), E = TrackedRegions.end (); I != E; ++I) {
325
+ SVal Val = I->second ;
326
+ for (auto si = Val.symbol_begin (), se = Val.symbol_end (); si != se; ++si) {
327
+ SR.markLive (*si);
328
+ }
329
+ }
330
+ }
331
+
256
332
void SmartPtrModeling::handleReset (const CallEvent &Call,
257
333
CheckerContext &C) const {
258
334
ProgramStateRef State = C.getState ();
@@ -275,8 +351,8 @@ void SmartPtrModeling::handleReset(const CallEvent &Call,
275
351
!BR.isInteresting (ThisRegion))
276
352
return ;
277
353
bugreporter::trackExpressionValue (BR.getErrorNode (), TrackingExpr, BR);
278
- OS << " Smart pointer " ;
279
- ThisRegion-> printPretty (OS);
354
+ OS << " Smart pointer" ;
355
+ checkAndPrettyPrintRegion (OS, ThisRegion );
280
356
OS << " reset using a null value" ;
281
357
}));
282
358
// TODO: Make sure to ivalidate the region in the Store if we don't have
@@ -310,8 +386,8 @@ void SmartPtrModeling::handleRelease(const CallEvent &Call,
310
386
!BR.isInteresting (ThisRegion))
311
387
return ;
312
388
313
- OS << " Smart pointer " ;
314
- ThisRegion-> printPretty (OS);
389
+ OS << " Smart pointer" ;
390
+ checkAndPrettyPrintRegion (OS, ThisRegion );
315
391
OS << " is released and set to null" ;
316
392
}));
317
393
// TODO: Add support to enable MallocChecker to start tracking the raw
@@ -350,10 +426,10 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call,
350
426
!BR.isInteresting (ThisRegion))
351
427
return ;
352
428
BR.markInteresting (ArgRegion);
353
- OS << " Swapped null smart pointer " ;
354
- ArgRegion-> printPretty (OS);
355
- OS << " with smart pointer " ;
356
- ThisRegion-> printPretty (OS);
429
+ OS << " Swapped null smart pointer" ;
430
+ checkAndPrettyPrintRegion (OS, ArgRegion );
431
+ OS << " with smart pointer" ;
432
+ checkAndPrettyPrintRegion (OS, ThisRegion );
357
433
}));
358
434
}
359
435
@@ -410,8 +486,8 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
410
486
if (&BR.getBugType () != smartptr::getNullDereferenceBugType () ||
411
487
!BR.isInteresting (ThisRegion))
412
488
return ;
413
- OS << " Smart pointer " ;
414
- ThisRegion-> printPretty (OS);
489
+ OS << " Smart pointer" ;
490
+ checkAndPrettyPrintRegion (OS, ThisRegion );
415
491
OS << " is assigned to null" ;
416
492
}));
417
493
return true ;
@@ -447,14 +523,14 @@ bool SmartPtrModeling::updateMovedSmartPointers(
447
523
if (&BR.getBugType () != smartptr::getNullDereferenceBugType ())
448
524
return ;
449
525
if (BR.isInteresting (OtherSmartPtrRegion)) {
450
- OS << " Smart pointer " ;
451
- OtherSmartPtrRegion-> printPretty (OS);
452
- OS << " is null after being moved to " ;
453
- ThisRegion-> printPretty (OS);
526
+ OS << " Smart pointer" ;
527
+ checkAndPrettyPrintRegion (OS, OtherSmartPtrRegion );
528
+ OS << " is null after being moved to" ;
529
+ checkAndPrettyPrintRegion (OS, ThisRegion );
454
530
}
455
531
if (BR.isInteresting (ThisRegion) && IsArgValNull) {
456
- OS << " A null pointer value is moved to " ;
457
- ThisRegion-> printPretty (OS);
532
+ OS << " A null pointer value is moved to" ;
533
+ checkAndPrettyPrintRegion (OS, ThisRegion );
458
534
BR.markInteresting (OtherSmartPtrRegion);
459
535
}
460
536
}));
@@ -471,16 +547,92 @@ bool SmartPtrModeling::updateMovedSmartPointers(
471
547
if (&BR.getBugType () != smartptr::getNullDereferenceBugType () ||
472
548
!BR.isInteresting (OtherSmartPtrRegion))
473
549
return ;
474
- OS << " Smart pointer " ;
475
- OtherSmartPtrRegion-> printPretty (OS);
476
- OS << " is null after; previous value moved to " ;
477
- ThisRegion-> printPretty (OS);
550
+ OS << " Smart pointer" ;
551
+ checkAndPrettyPrintRegion (OS, OtherSmartPtrRegion );
552
+ OS << " is null after; previous value moved to" ;
553
+ checkAndPrettyPrintRegion (OS, ThisRegion );
478
554
}));
479
555
return true ;
480
556
}
481
557
return false ;
482
558
}
483
559
560
+ void SmartPtrModeling::handleBoolConversion (const CallEvent &Call,
561
+ CheckerContext &C) const {
562
+ // To model unique_ptr::operator bool
563
+ ProgramStateRef State = C.getState ();
564
+ const Expr *CallExpr = Call.getOriginExpr ();
565
+ const MemRegion *ThisRegion =
566
+ cast<CXXInstanceCall>(&Call)->getCXXThisVal ().getAsRegion ();
567
+
568
+ SVal InnerPointerVal;
569
+ if (const auto *InnerValPtr = State->get <TrackedRegionMap>(ThisRegion)) {
570
+ InnerPointerVal = *InnerValPtr;
571
+ } else {
572
+ // In case of inner pointer SVal is not available we create
573
+ // conjureSymbolVal for inner pointer value.
574
+ auto InnerPointerType = getInnerPointerType (Call, C);
575
+ if (InnerPointerType.isNull ())
576
+ return ;
577
+
578
+ const LocationContext *LC = C.getLocationContext ();
579
+ InnerPointerVal = C.getSValBuilder ().conjureSymbolVal (
580
+ CallExpr, LC, InnerPointerType, C.blockCount ());
581
+ State = State->set <TrackedRegionMap>(ThisRegion, InnerPointerVal);
582
+ }
583
+
584
+ if (State->isNull (InnerPointerVal).isConstrainedTrue ()) {
585
+ State = State->BindExpr (CallExpr, C.getLocationContext (),
586
+ C.getSValBuilder ().makeTruthVal (false ));
587
+
588
+ C.addTransition (State);
589
+ return ;
590
+ } else if (State->isNonNull (InnerPointerVal).isConstrainedTrue ()) {
591
+ State = State->BindExpr (CallExpr, C.getLocationContext (),
592
+ C.getSValBuilder ().makeTruthVal (true ));
593
+
594
+ C.addTransition (State);
595
+ return ;
596
+ } else if (move::isMovedFrom (State, ThisRegion)) {
597
+ C.addTransition (
598
+ State->BindExpr (CallExpr, C.getLocationContext (),
599
+ C.getSValBuilder ().makeZeroVal (Call.getResultType ())));
600
+ return ;
601
+ } else {
602
+ ProgramStateRef NotNullState, NullState;
603
+ std::tie (NotNullState, NullState) =
604
+ State->assume (InnerPointerVal.castAs <DefinedOrUnknownSVal>());
605
+
606
+ auto NullVal = C.getSValBuilder ().makeNull ();
607
+ // Explicitly tracking the region as null.
608
+ NullState = NullState->set <TrackedRegionMap>(ThisRegion, NullVal);
609
+
610
+ NullState = NullState->BindExpr (CallExpr, C.getLocationContext (),
611
+ C.getSValBuilder ().makeTruthVal (false ));
612
+ C.addTransition (NullState, C.getNoteTag (
613
+ [ThisRegion](PathSensitiveBugReport &BR,
614
+ llvm::raw_ostream &OS) {
615
+ OS << " Assuming smart pointer" ;
616
+ checkAndPrettyPrintRegion (OS, ThisRegion);
617
+ OS << " is null" ;
618
+ },
619
+ /* IsPrunable=*/ true ));
620
+ NotNullState =
621
+ NotNullState->BindExpr (CallExpr, C.getLocationContext (),
622
+ C.getSValBuilder ().makeTruthVal (true ));
623
+ C.addTransition (
624
+ NotNullState,
625
+ C.getNoteTag (
626
+ [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
627
+ OS << " Assuming smart pointer" ;
628
+ checkAndPrettyPrintRegion (OS, ThisRegion);
629
+ OS << " is non-null" ;
630
+ },
631
+ /* IsPrunable=*/ true ));
632
+ return ;
633
+ }
634
+ }
635
+
484
636
void ento::registerSmartPtrModeling (CheckerManager &Mgr) {
485
637
auto *Checker = Mgr.registerChecker <SmartPtrModeling>();
486
638
Checker->ModelSmartPtrDereference =
0 commit comments