Skip to content

Commit bc3d4d9

Browse files
committed
[analyzer] Add bool operator modeling for unque_ptr
Summary: Implemented boolean conversion operator for unique_ptr Reviewers: NoQ, Szelethus, vsavchenko, xazax.hun Reviewed By: NoQ, xazax.hun Subscribers: martong, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D86027
1 parent 5475154 commit bc3d4d9

File tree

3 files changed

+371
-39
lines changed

3 files changed

+371
-39
lines changed

clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp

Lines changed: 190 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "SmartPtr.h"
1616

1717
#include "clang/AST/DeclCXX.h"
18+
#include "clang/AST/DeclarationName.h"
1819
#include "clang/AST/ExprCXX.h"
1920
#include "clang/AST/Type.h"
2021
#include "clang/Basic/LLVM.h"
@@ -35,9 +36,10 @@ using namespace ento;
3536

3637
namespace {
3738
class SmartPtrModeling
38-
: public Checker<eval::Call, check::DeadSymbols, check::RegionChanges> {
39+
: public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40+
check::LiveSymbols> {
3941

40-
bool isAssignOpMethod(const CallEvent &Call) const;
42+
bool isBoolConversionMethod(const CallEvent &Call) const;
4143

4244
public:
4345
// Whether the checker should model for null dereferences of smart pointers.
@@ -51,6 +53,9 @@ class SmartPtrModeling
5153
ArrayRef<const MemRegion *> ExplicitRegions,
5254
ArrayRef<const MemRegion *> Regions,
5355
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;
5459

5560
private:
5661
void handleReset(const CallEvent &Call, CheckerContext &C) const;
@@ -62,6 +67,7 @@ class SmartPtrModeling
6267
const MemRegion *ThisRegion) const;
6368
bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
6469
const MemRegion *OtherSmartPtrRegion) const;
70+
void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
6571

6672
using SmartPtrMethodHandlerFn =
6773
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
@@ -128,7 +134,38 @@ static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
128134
return State;
129135
}
130136

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 {
132169
// TODO: Update CallDescription to support anonymous calls?
133170
// TODO: Handle other methods, such as .get() or .release().
134171
// But once we do, we'd need a visitor to explain null dereferences
@@ -143,21 +180,31 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
143180
if (!smartptr::isStdSmartPtrCall(Call))
144181
return false;
145182

146-
if (isAssignOpMethod(Call)) {
183+
if (isBoolConversionMethod(Call)) {
147184
const MemRegion *ThisR =
148185
cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
149186

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;
154207
}
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;
161208
}
162209

163210
if (!ModelSmartPtrDereference)
@@ -184,8 +231,8 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
184231
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
185232
!BR.isInteresting(ThisRegion))
186233
return;
187-
OS << "Default constructed smart pointer ";
188-
ThisRegion->printPretty(OS);
234+
OS << "Default constructed smart pointer";
235+
checkAndPrettyPrintRegion(OS, ThisRegion);
189236
OS << " is null";
190237
}));
191238
} else {
@@ -202,8 +249,8 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
202249
!BR.isInteresting(ThisRegion))
203250
return;
204251
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
205-
OS << "Smart pointer ";
206-
ThisRegion->printPretty(OS);
252+
OS << "Smart pointer";
253+
checkAndPrettyPrintRegion(OS, ThisRegion);
207254
if (ArgVal.isZeroConstant())
208255
OS << " is constructed using a null value";
209256
else
@@ -239,6 +286,23 @@ void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
239286
C.addTransition(State);
240287
}
241288

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+
242306
ProgramStateRef SmartPtrModeling::checkRegionChanges(
243307
ProgramStateRef State, const InvalidatedSymbols *Invalidated,
244308
ArrayRef<const MemRegion *> ExplicitRegions,
@@ -253,6 +317,18 @@ ProgramStateRef SmartPtrModeling::checkRegionChanges(
253317
return State->set<TrackedRegionMap>(RegionMap);
254318
}
255319

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+
256332
void SmartPtrModeling::handleReset(const CallEvent &Call,
257333
CheckerContext &C) const {
258334
ProgramStateRef State = C.getState();
@@ -275,8 +351,8 @@ void SmartPtrModeling::handleReset(const CallEvent &Call,
275351
!BR.isInteresting(ThisRegion))
276352
return;
277353
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
278-
OS << "Smart pointer ";
279-
ThisRegion->printPretty(OS);
354+
OS << "Smart pointer";
355+
checkAndPrettyPrintRegion(OS, ThisRegion);
280356
OS << " reset using a null value";
281357
}));
282358
// 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,
310386
!BR.isInteresting(ThisRegion))
311387
return;
312388

313-
OS << "Smart pointer ";
314-
ThisRegion->printPretty(OS);
389+
OS << "Smart pointer";
390+
checkAndPrettyPrintRegion(OS, ThisRegion);
315391
OS << " is released and set to null";
316392
}));
317393
// TODO: Add support to enable MallocChecker to start tracking the raw
@@ -350,10 +426,10 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call,
350426
!BR.isInteresting(ThisRegion))
351427
return;
352428
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);
357433
}));
358434
}
359435

@@ -410,8 +486,8 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
410486
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
411487
!BR.isInteresting(ThisRegion))
412488
return;
413-
OS << "Smart pointer ";
414-
ThisRegion->printPretty(OS);
489+
OS << "Smart pointer";
490+
checkAndPrettyPrintRegion(OS, ThisRegion);
415491
OS << " is assigned to null";
416492
}));
417493
return true;
@@ -447,14 +523,14 @@ bool SmartPtrModeling::updateMovedSmartPointers(
447523
if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
448524
return;
449525
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);
454530
}
455531
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);
458534
BR.markInteresting(OtherSmartPtrRegion);
459535
}
460536
}));
@@ -471,16 +547,92 @@ bool SmartPtrModeling::updateMovedSmartPointers(
471547
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
472548
!BR.isInteresting(OtherSmartPtrRegion))
473549
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);
478554
}));
479555
return true;
480556
}
481557
return false;
482558
}
483559

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+
484636
void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
485637
auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
486638
Checker->ModelSmartPtrDereference =

0 commit comments

Comments
 (0)