1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a checker that models various aspects of
10 // C++ smart pointer behavior.
12 //===----------------------------------------------------------------------===//
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/DeclarationName.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/Type.h"
21 #include "clang/Basic/LLVM.h"
22 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
32 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
33 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
34 #include "llvm/ADT/STLExtras.h"
35 #include "llvm/Support/ErrorHandling.h"
39 using namespace clang
;
44 class SmartPtrModeling
45 : public Checker
<eval::Call
, check::DeadSymbols
, check::RegionChanges
,
48 bool isBoolConversionMethod(const CallEvent
&Call
) const;
51 // Whether the checker should model for null dereferences of smart pointers.
52 bool ModelSmartPtrDereference
= false;
53 bool evalCall(const CallEvent
&Call
, CheckerContext
&C
) const;
54 void checkDeadSymbols(SymbolReaper
&SymReaper
, CheckerContext
&C
) const;
56 checkRegionChanges(ProgramStateRef State
,
57 const InvalidatedSymbols
*Invalidated
,
58 ArrayRef
<const MemRegion
*> ExplicitRegions
,
59 ArrayRef
<const MemRegion
*> Regions
,
60 const LocationContext
*LCtx
, const CallEvent
*Call
) const;
61 void printState(raw_ostream
&Out
, ProgramStateRef State
, const char *NL
,
62 const char *Sep
) const override
;
63 void checkLiveSymbols(ProgramStateRef State
, SymbolReaper
&SR
) const;
66 void handleReset(const CallEvent
&Call
, CheckerContext
&C
) const;
67 void handleRelease(const CallEvent
&Call
, CheckerContext
&C
) const;
68 void handleSwapMethod(const CallEvent
&Call
, CheckerContext
&C
) const;
69 void handleGet(const CallEvent
&Call
, CheckerContext
&C
) const;
70 bool handleAssignOp(const CallEvent
&Call
, CheckerContext
&C
) const;
71 bool handleMoveCtr(const CallEvent
&Call
, CheckerContext
&C
,
72 const MemRegion
*ThisRegion
) const;
73 bool updateMovedSmartPointers(CheckerContext
&C
, const MemRegion
*ThisRegion
,
74 const MemRegion
*OtherSmartPtrRegion
,
75 const CallEvent
&Call
) const;
76 void handleBoolConversion(const CallEvent
&Call
, CheckerContext
&C
) const;
77 bool handleComparisionOp(const CallEvent
&Call
, CheckerContext
&C
) const;
78 bool handleOstreamOperator(const CallEvent
&Call
, CheckerContext
&C
) const;
79 bool handleSwap(ProgramStateRef State
, SVal First
, SVal Second
,
80 CheckerContext
&C
) const;
81 std::pair
<SVal
, ProgramStateRef
>
82 retrieveOrConjureInnerPtrVal(ProgramStateRef State
,
83 const MemRegion
*ThisRegion
, const Expr
*E
,
84 QualType Type
, CheckerContext
&C
) const;
86 using SmartPtrMethodHandlerFn
=
87 void (SmartPtrModeling::*)(const CallEvent
&Call
, CheckerContext
&) const;
88 CallDescriptionMap
<SmartPtrMethodHandlerFn
> SmartPtrMethodHandlers
{
89 {{CDM::CXXMethod
, {"reset"}}, &SmartPtrModeling::handleReset
},
90 {{CDM::CXXMethod
, {"release"}}, &SmartPtrModeling::handleRelease
},
91 {{CDM::CXXMethod
, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod
},
92 {{CDM::CXXMethod
, {"get"}}, &SmartPtrModeling::handleGet
}};
93 const CallDescription StdSwapCall
{CDM::SimpleFunc
, {"std", "swap"}, 2};
94 const CallDescriptionSet MakeUniqueVariants
{
95 {CDM::SimpleFunc
, {"std", "make_unique"}},
96 {CDM::SimpleFunc
, {"std", "make_unique_for_overwrite"}}};
98 } // end of anonymous namespace
100 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap
, const MemRegion
*, SVal
)
102 // Checks if RD has name in Names and is in std namespace
103 static bool hasStdClassWithName(const CXXRecordDecl
*RD
,
104 ArrayRef
<llvm::StringLiteral
> Names
) {
105 if (!RD
|| !RD
->getDeclContext()->isStdNamespace())
107 if (RD
->getDeclName().isIdentifier())
108 return llvm::is_contained(Names
, RD
->getName());
112 constexpr llvm::StringLiteral STD_PTR_NAMES
[] = {"shared_ptr", "unique_ptr",
115 static bool isStdSmartPtr(const CXXRecordDecl
*RD
) {
116 return hasStdClassWithName(RD
, STD_PTR_NAMES
);
119 static bool isStdSmartPtr(const Expr
*E
) {
120 return isStdSmartPtr(E
->getType()->getAsCXXRecordDecl());
123 // Define the inter-checker API.
127 bool isStdSmartPtrCall(const CallEvent
&Call
) {
128 const auto *MethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(Call
.getDecl());
129 if (!MethodDecl
|| !MethodDecl
->getParent())
131 return isStdSmartPtr(MethodDecl
->getParent());
134 bool isStdSmartPtr(const CXXRecordDecl
*RD
) {
135 if (!RD
|| !RD
->getDeclContext()->isStdNamespace())
138 if (RD
->getDeclName().isIdentifier()) {
139 StringRef Name
= RD
->getName();
140 return Name
== "shared_ptr" || Name
== "unique_ptr" || Name
== "weak_ptr";
145 bool isStdSmartPtr(const Expr
*E
) {
146 return isStdSmartPtr(E
->getType()->getAsCXXRecordDecl());
149 bool isNullSmartPtr(const ProgramStateRef State
, const MemRegion
*ThisRegion
) {
150 const auto *InnerPointVal
= State
->get
<TrackedRegionMap
>(ThisRegion
);
151 return InnerPointVal
&&
152 !State
->assume(InnerPointVal
->castAs
<DefinedOrUnknownSVal
>(), true);
154 } // namespace smartptr
158 // If a region is removed all of the subregions need to be removed too.
159 static TrackedRegionMapTy
160 removeTrackedSubregions(TrackedRegionMapTy RegionMap
,
161 TrackedRegionMapTy::Factory
&RegionMapFactory
,
162 const MemRegion
*Region
) {
165 for (const auto &E
: RegionMap
) {
166 if (E
.first
->isSubRegionOf(Region
))
167 RegionMap
= RegionMapFactory
.remove(RegionMap
, E
.first
);
172 static ProgramStateRef
updateSwappedRegion(ProgramStateRef State
,
173 const MemRegion
*Region
,
174 const SVal
*RegionInnerPointerVal
) {
175 if (RegionInnerPointerVal
) {
176 State
= State
->set
<TrackedRegionMap
>(Region
, *RegionInnerPointerVal
);
178 State
= State
->remove
<TrackedRegionMap
>(Region
);
183 static QualType
getInnerPointerType(CheckerContext C
, const CXXRecordDecl
*RD
) {
184 if (!RD
|| !RD
->isInStdNamespace())
187 const auto *TSD
= dyn_cast
<ClassTemplateSpecializationDecl
>(RD
);
191 auto TemplateArgs
= TSD
->getTemplateArgs().asArray();
192 if (TemplateArgs
.empty())
194 auto InnerValueType
= TemplateArgs
[0].getAsType();
195 return C
.getASTContext().getPointerType(InnerValueType
.getCanonicalType());
198 // This is for use with standalone-functions like std::make_unique,
199 // std::make_unique_for_overwrite, etc. It reads the template parameter and
200 // returns the pointer type corresponding to it,
201 static QualType
getPointerTypeFromTemplateArg(const CallEvent
&Call
,
203 const auto *FD
= dyn_cast_or_null
<FunctionDecl
>(Call
.getDecl());
204 if (!FD
|| !FD
->getPrimaryTemplate())
206 const auto &TemplateArgs
= FD
->getTemplateSpecializationArgs()->asArray();
207 if (TemplateArgs
.size() == 0)
209 auto ValueType
= TemplateArgs
[0].getAsType();
210 return C
.getASTContext().getPointerType(ValueType
.getCanonicalType());
213 // Helper method to get the inner pointer type of specialized smart pointer
214 // Returns empty type if not found valid inner pointer type.
215 static QualType
getInnerPointerType(const CallEvent
&Call
, CheckerContext
&C
) {
216 const auto *MethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(Call
.getDecl());
217 if (!MethodDecl
|| !MethodDecl
->getParent())
220 const auto *RecordDecl
= MethodDecl
->getParent();
221 return getInnerPointerType(C
, RecordDecl
);
224 // Helper method to pretty print region and avoid extra spacing.
225 static void checkAndPrettyPrintRegion(llvm::raw_ostream
&OS
,
226 const MemRegion
*Region
) {
227 if (Region
->canPrintPretty()) {
229 Region
->printPretty(OS
);
233 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent
&Call
) const {
234 // TODO: Update CallDescription to support anonymous calls?
235 // TODO: Handle other methods, such as .get() or .release().
236 // But once we do, we'd need a visitor to explain null dereferences
237 // that are found via such modeling.
238 const auto *CD
= dyn_cast_or_null
<CXXConversionDecl
>(Call
.getDecl());
239 return CD
&& CD
->getConversionType()->isBooleanType();
242 constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES
[] = {"basic_ostream"};
244 static bool isStdBasicOstream(const Expr
*E
) {
245 const auto *RD
= E
->getType()->getAsCXXRecordDecl();
246 return hasStdClassWithName(RD
, BASIC_OSTREAM_NAMES
);
249 static bool isStdFunctionCall(const CallEvent
&Call
) {
250 return Call
.getDecl() && Call
.getDecl()->getDeclContext()->isStdNamespace();
253 static bool isStdOstreamOperatorCall(const CallEvent
&Call
) {
254 if (Call
.getNumArgs() != 2 || !isStdFunctionCall(Call
))
256 const auto *FC
= dyn_cast
<SimpleFunctionCall
>(&Call
);
259 const FunctionDecl
*FD
= FC
->getDecl();
260 if (!FD
->isOverloadedOperator())
262 const OverloadedOperatorKind OOK
= FD
->getOverloadedOperator();
263 if (OOK
!= clang::OO_LessLess
)
265 return isStdSmartPtr(Call
.getArgExpr(1)) &&
266 isStdBasicOstream(Call
.getArgExpr(0));
269 static bool isPotentiallyComparisionOpCall(const CallEvent
&Call
) {
270 if (Call
.getNumArgs() != 2 || !isStdFunctionCall(Call
))
272 return smartptr::isStdSmartPtr(Call
.getArgExpr(0)) ||
273 smartptr::isStdSmartPtr(Call
.getArgExpr(1));
276 bool SmartPtrModeling::evalCall(const CallEvent
&Call
,
277 CheckerContext
&C
) const {
279 ProgramStateRef State
= C
.getState();
281 // If any one of the arg is a unique_ptr, then
282 // we can try this function
283 if (ModelSmartPtrDereference
&& isPotentiallyComparisionOpCall(Call
))
284 if (handleComparisionOp(Call
, C
))
287 if (ModelSmartPtrDereference
&& isStdOstreamOperatorCall(Call
))
288 return handleOstreamOperator(Call
, C
);
290 if (StdSwapCall
.matches(Call
)) {
291 // Check the first arg, if it is of std::unique_ptr type.
292 assert(Call
.getNumArgs() == 2 && "std::swap should have two arguments");
293 const Expr
*FirstArg
= Call
.getArgExpr(0);
294 if (!smartptr::isStdSmartPtr(FirstArg
->getType()->getAsCXXRecordDecl()))
296 return handleSwap(State
, Call
.getArgSVal(0), Call
.getArgSVal(1), C
);
299 if (MakeUniqueVariants
.contains(Call
)) {
300 if (!ModelSmartPtrDereference
)
303 const std::optional
<SVal
> ThisRegionOpt
=
304 Call
.getReturnValueUnderConstruction();
308 const auto PtrVal
= C
.getSValBuilder().getConjuredHeapSymbolVal(
309 Call
.getOriginExpr(), C
.getLocationContext(),
310 getPointerTypeFromTemplateArg(Call
, C
), C
.blockCount());
312 const MemRegion
*ThisRegion
= ThisRegionOpt
->getAsRegion();
313 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, PtrVal
);
314 State
= State
->assume(PtrVal
, true);
316 // TODO: ExprEngine should do this for us.
317 // For a bit more context:
318 // 1) Why do we need this? Since we are modelling a "function"
319 // that returns a constructed object we need to store this information in
320 // the program state.
322 // 2) Why does this work?
323 // `updateObjectsUnderConstruction` does exactly as it sounds.
325 // 3) How should it look like when moved to the Engine?
326 // It would be nice if we can just
327 // pretend we don't need to know about this - ie, completely automatic work.
328 // However, realistically speaking, I think we would need to "signal" the
329 // ExprEngine evalCall handler that we are constructing an object with this
330 // function call (constructors obviously construct, hence can be
331 // automatically deduced).
332 auto &Engine
= State
->getStateManager().getOwningEngine();
333 State
= Engine
.updateObjectsUnderConstruction(
334 *ThisRegionOpt
, nullptr, State
, C
.getLocationContext(),
335 Call
.getConstructionContext(), {});
337 // We don't leave a note here since it is guaranteed the
338 // unique_ptr from this call is non-null (hence is safe to de-reference).
339 C
.addTransition(State
);
343 if (!smartptr::isStdSmartPtrCall(Call
))
346 if (isBoolConversionMethod(Call
)) {
347 const MemRegion
*ThisR
=
348 cast
<CXXInstanceCall
>(&Call
)->getCXXThisVal().getAsRegion();
350 if (ModelSmartPtrDereference
) {
351 // The check for the region is moved is duplicated in handleBoolOperation
353 // FIXME: Once we model std::move for smart pointers clean up this and use
355 handleBoolConversion(Call
, C
);
358 if (!move::isMovedFrom(State
, ThisR
)) {
359 // TODO: Model this case as well. At least, avoid invalidation of
364 // TODO: Add a note to bug reports describing this decision.
365 C
.addTransition(State
->BindExpr(
366 Call
.getOriginExpr(), C
.getLocationContext(),
367 C
.getSValBuilder().makeZeroVal(Call
.getResultType())));
373 if (!ModelSmartPtrDereference
)
376 if (const auto *CC
= dyn_cast
<CXXConstructorCall
>(&Call
)) {
377 if (CC
->getDecl()->isCopyConstructor())
380 const MemRegion
*ThisRegion
= CC
->getCXXThisVal().getAsRegion();
384 QualType ThisType
= cast
<CXXMethodDecl
>(Call
.getDecl())->getThisType();
386 if (CC
->getDecl()->isMoveConstructor())
387 return handleMoveCtr(Call
, C
, ThisRegion
);
389 if (Call
.getNumArgs() == 0) {
390 auto NullVal
= C
.getSValBuilder().makeNullWithType(ThisType
);
391 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, NullVal
);
394 State
, C
.getNoteTag([ThisRegion
](PathSensitiveBugReport
&BR
,
395 llvm::raw_ostream
&OS
) {
396 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
397 !BR
.isInteresting(ThisRegion
))
399 OS
<< "Default constructed smart pointer";
400 checkAndPrettyPrintRegion(OS
, ThisRegion
);
404 const auto *TrackingExpr
= Call
.getArgExpr(0);
405 assert(TrackingExpr
->getType()->isPointerType() &&
406 "Adding a non pointer value to TrackedRegionMap");
407 auto ArgVal
= Call
.getArgSVal(0);
408 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, ArgVal
);
410 C
.addTransition(State
, C
.getNoteTag([ThisRegion
, TrackingExpr
,
411 ArgVal
](PathSensitiveBugReport
&BR
,
412 llvm::raw_ostream
&OS
) {
413 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
414 !BR
.isInteresting(ThisRegion
))
416 bugreporter::trackExpressionValue(BR
.getErrorNode(), TrackingExpr
, BR
);
417 OS
<< "Smart pointer";
418 checkAndPrettyPrintRegion(OS
, ThisRegion
);
419 if (ArgVal
.isZeroConstant())
420 OS
<< " is constructed using a null value";
422 OS
<< " is constructed";
428 if (handleAssignOp(Call
, C
))
431 const SmartPtrMethodHandlerFn
*Handler
= SmartPtrMethodHandlers
.lookup(Call
);
434 (this->**Handler
)(Call
, C
);
436 return C
.isDifferent();
439 std::pair
<SVal
, ProgramStateRef
> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440 ProgramStateRef State
, const MemRegion
*ThisRegion
, const Expr
*E
,
441 QualType Type
, CheckerContext
&C
) const {
442 const auto *Ptr
= State
->get
<TrackedRegionMap
>(ThisRegion
);
444 return {*Ptr
, State
};
445 auto Val
= C
.getSValBuilder().conjureSymbolVal(E
, C
.getLocationContext(),
446 Type
, C
.blockCount());
447 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, Val
);
451 bool SmartPtrModeling::handleComparisionOp(const CallEvent
&Call
,
452 CheckerContext
&C
) const {
453 const auto *FC
= dyn_cast
<SimpleFunctionCall
>(&Call
);
456 const FunctionDecl
*FD
= FC
->getDecl();
457 if (!FD
->isOverloadedOperator())
459 const OverloadedOperatorKind OOK
= FD
->getOverloadedOperator();
460 if (!(OOK
== OO_EqualEqual
|| OOK
== OO_ExclaimEqual
|| OOK
== OO_Less
||
461 OOK
== OO_LessEqual
|| OOK
== OO_Greater
|| OOK
== OO_GreaterEqual
||
462 OOK
== OO_Spaceship
))
465 // There are some special cases about which we can infer about
466 // the resulting answer.
467 // For reference, there is a discussion at https://reviews.llvm.org/D104616.
468 // Also, the cppreference page is good to look at
469 // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
471 auto makeSValFor
= [&C
, this](ProgramStateRef State
, const Expr
*E
,
472 SVal S
) -> std::pair
<SVal
, ProgramStateRef
> {
473 if (S
.isZeroConstant()) {
476 const MemRegion
*Reg
= S
.getAsRegion();
478 "this pointer of std::unique_ptr should be obtainable as MemRegion");
479 QualType Type
= getInnerPointerType(C
, E
->getType()->getAsCXXRecordDecl());
480 return retrieveOrConjureInnerPtrVal(State
, Reg
, E
, Type
, C
);
483 SVal First
= Call
.getArgSVal(0);
484 SVal Second
= Call
.getArgSVal(1);
485 const auto *FirstExpr
= Call
.getArgExpr(0);
486 const auto *SecondExpr
= Call
.getArgExpr(1);
488 const auto *ResultExpr
= Call
.getOriginExpr();
489 const auto *LCtx
= C
.getLocationContext();
490 auto &Bldr
= C
.getSValBuilder();
491 ProgramStateRef State
= C
.getState();
493 SVal FirstPtrVal
, SecondPtrVal
;
494 std::tie(FirstPtrVal
, State
) = makeSValFor(State
, FirstExpr
, First
);
495 std::tie(SecondPtrVal
, State
) = makeSValFor(State
, SecondExpr
, Second
);
496 BinaryOperatorKind BOK
=
497 operationKindFromOverloadedOperator(OOK
, true).GetBinaryOpUnsafe();
498 auto RetVal
= Bldr
.evalBinOp(State
, BOK
, FirstPtrVal
, SecondPtrVal
,
499 Call
.getResultType());
501 if (OOK
!= OO_Spaceship
) {
502 ProgramStateRef TrueState
, FalseState
;
503 std::tie(TrueState
, FalseState
) =
504 State
->assume(*RetVal
.getAs
<DefinedOrUnknownSVal
>());
507 TrueState
->BindExpr(ResultExpr
, LCtx
, Bldr
.makeTruthVal(true)));
510 FalseState
->BindExpr(ResultExpr
, LCtx
, Bldr
.makeTruthVal(false)));
512 C
.addTransition(State
->BindExpr(ResultExpr
, LCtx
, RetVal
));
517 bool SmartPtrModeling::handleOstreamOperator(const CallEvent
&Call
,
518 CheckerContext
&C
) const {
519 // operator<< does not modify the smart pointer.
520 // And we don't really have much of modelling of basic_ostream.
521 // So, we are better off:
522 // 1) Invalidating the mem-region of the ostream object at hand.
523 // 2) Setting the SVal of the basic_ostream as the return value.
524 // Not very satisfying, but it gets the job done, and is better
525 // than the default handling. :)
527 ProgramStateRef State
= C
.getState();
528 const auto StreamVal
= Call
.getArgSVal(0);
529 const MemRegion
*StreamThisRegion
= StreamVal
.getAsRegion();
530 if (!StreamThisRegion
)
533 State
->invalidateRegions({StreamThisRegion
}, Call
.getOriginExpr(),
534 C
.blockCount(), C
.getLocationContext(), false);
536 State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(), StreamVal
);
537 C
.addTransition(State
);
541 void SmartPtrModeling::checkDeadSymbols(SymbolReaper
&SymReaper
,
542 CheckerContext
&C
) const {
543 ProgramStateRef State
= C
.getState();
544 // Clean up dead regions from the region map.
545 TrackedRegionMapTy TrackedRegions
= State
->get
<TrackedRegionMap
>();
546 for (auto E
: TrackedRegions
) {
547 const MemRegion
*Region
= E
.first
;
548 bool IsRegDead
= !SymReaper
.isLiveRegion(Region
);
551 State
= State
->remove
<TrackedRegionMap
>(Region
);
553 C
.addTransition(State
);
556 void SmartPtrModeling::printState(raw_ostream
&Out
, ProgramStateRef State
,
557 const char *NL
, const char *Sep
) const {
558 TrackedRegionMapTy RS
= State
->get
<TrackedRegionMap
>();
561 Out
<< Sep
<< "Smart ptr regions :" << NL
;
563 I
.first
->dumpToStream(Out
);
564 if (smartptr::isNullSmartPtr(State
, I
.first
))
573 ProgramStateRef
SmartPtrModeling::checkRegionChanges(
574 ProgramStateRef State
, const InvalidatedSymbols
*Invalidated
,
575 ArrayRef
<const MemRegion
*> ExplicitRegions
,
576 ArrayRef
<const MemRegion
*> Regions
, const LocationContext
*LCtx
,
577 const CallEvent
*Call
) const {
578 TrackedRegionMapTy RegionMap
= State
->get
<TrackedRegionMap
>();
579 TrackedRegionMapTy::Factory
&RegionMapFactory
=
580 State
->get_context
<TrackedRegionMap
>();
581 for (const auto *Region
: Regions
)
582 RegionMap
= removeTrackedSubregions(RegionMap
, RegionMapFactory
,
583 Region
->getBaseRegion());
584 return State
->set
<TrackedRegionMap
>(RegionMap
);
587 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State
,
588 SymbolReaper
&SR
) const {
589 // Marking tracked symbols alive
590 TrackedRegionMapTy TrackedRegions
= State
->get
<TrackedRegionMap
>();
591 for (SVal Val
: llvm::make_second_range(TrackedRegions
)) {
592 for (SymbolRef Sym
: Val
.symbols()) {
598 void SmartPtrModeling::handleReset(const CallEvent
&Call
,
599 CheckerContext
&C
) const {
600 ProgramStateRef State
= C
.getState();
601 const auto *IC
= dyn_cast
<CXXInstanceCall
>(&Call
);
605 const MemRegion
*ThisRegion
= IC
->getCXXThisVal().getAsRegion();
609 assert(Call
.getArgExpr(0)->getType()->isPointerType() &&
610 "Adding a non pointer value to TrackedRegionMap");
611 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, Call
.getArgSVal(0));
612 const auto *TrackingExpr
= Call
.getArgExpr(0);
614 State
, C
.getNoteTag([ThisRegion
, TrackingExpr
](PathSensitiveBugReport
&BR
,
615 llvm::raw_ostream
&OS
) {
616 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
617 !BR
.isInteresting(ThisRegion
))
619 bugreporter::trackExpressionValue(BR
.getErrorNode(), TrackingExpr
, BR
);
620 OS
<< "Smart pointer";
621 checkAndPrettyPrintRegion(OS
, ThisRegion
);
622 OS
<< " reset using a null value";
624 // TODO: Make sure to ivalidate the region in the Store if we don't have
625 // time to model all methods.
628 void SmartPtrModeling::handleRelease(const CallEvent
&Call
,
629 CheckerContext
&C
) const {
630 ProgramStateRef State
= C
.getState();
631 const auto *IC
= dyn_cast
<CXXInstanceCall
>(&Call
);
635 const MemRegion
*ThisRegion
= IC
->getCXXThisVal().getAsRegion();
639 const auto *InnerPointVal
= State
->get
<TrackedRegionMap
>(ThisRegion
);
642 State
= State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(),
646 QualType ThisType
= cast
<CXXMethodDecl
>(Call
.getDecl())->getThisType();
647 auto ValueToUpdate
= C
.getSValBuilder().makeNullWithType(ThisType
);
648 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, ValueToUpdate
);
650 C
.addTransition(State
, C
.getNoteTag([ThisRegion
](PathSensitiveBugReport
&BR
,
651 llvm::raw_ostream
&OS
) {
652 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
653 !BR
.isInteresting(ThisRegion
))
656 OS
<< "Smart pointer";
657 checkAndPrettyPrintRegion(OS
, ThisRegion
);
658 OS
<< " is released and set to null";
660 // TODO: Add support to enable MallocChecker to start tracking the raw
664 void SmartPtrModeling::handleSwapMethod(const CallEvent
&Call
,
665 CheckerContext
&C
) const {
666 // To model unique_ptr::swap() method.
667 const auto *IC
= dyn_cast
<CXXInstanceCall
>(&Call
);
671 auto State
= C
.getState();
672 handleSwap(State
, IC
->getCXXThisVal(), Call
.getArgSVal(0), C
);
675 bool SmartPtrModeling::handleSwap(ProgramStateRef State
, SVal First
,
676 SVal Second
, CheckerContext
&C
) const {
677 const MemRegion
*FirstThisRegion
= First
.getAsRegion();
678 if (!FirstThisRegion
)
680 const MemRegion
*SecondThisRegion
= Second
.getAsRegion();
681 if (!SecondThisRegion
)
684 const auto *FirstInnerPtrVal
= State
->get
<TrackedRegionMap
>(FirstThisRegion
);
685 const auto *SecondInnerPtrVal
=
686 State
->get
<TrackedRegionMap
>(SecondThisRegion
);
688 State
= updateSwappedRegion(State
, FirstThisRegion
, SecondInnerPtrVal
);
689 State
= updateSwappedRegion(State
, SecondThisRegion
, FirstInnerPtrVal
);
691 C
.addTransition(State
, C
.getNoteTag([FirstThisRegion
, SecondThisRegion
](
692 PathSensitiveBugReport
&BR
,
693 llvm::raw_ostream
&OS
) {
694 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType())
696 if (BR
.isInteresting(FirstThisRegion
) &&
697 !BR
.isInteresting(SecondThisRegion
)) {
698 BR
.markInteresting(SecondThisRegion
);
699 BR
.markNotInteresting(FirstThisRegion
);
701 if (BR
.isInteresting(SecondThisRegion
) &&
702 !BR
.isInteresting(FirstThisRegion
)) {
703 BR
.markInteresting(FirstThisRegion
);
704 BR
.markNotInteresting(SecondThisRegion
);
706 // TODO: We need to emit some note here probably!!
712 void SmartPtrModeling::handleGet(const CallEvent
&Call
,
713 CheckerContext
&C
) const {
714 ProgramStateRef State
= C
.getState();
715 const auto *IC
= dyn_cast
<CXXInstanceCall
>(&Call
);
719 const MemRegion
*ThisRegion
= IC
->getCXXThisVal().getAsRegion();
723 SVal InnerPointerVal
;
724 std::tie(InnerPointerVal
, State
) = retrieveOrConjureInnerPtrVal(
725 State
, ThisRegion
, Call
.getOriginExpr(), Call
.getResultType(), C
);
726 State
= State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(),
728 // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
729 C
.addTransition(State
);
732 bool SmartPtrModeling::handleAssignOp(const CallEvent
&Call
,
733 CheckerContext
&C
) const {
734 ProgramStateRef State
= C
.getState();
735 const auto *OC
= dyn_cast
<CXXMemberOperatorCall
>(&Call
);
738 OverloadedOperatorKind OOK
= OC
->getOverloadedOperator();
741 const MemRegion
*ThisRegion
= OC
->getCXXThisVal().getAsRegion();
745 QualType ThisType
= cast
<CXXMethodDecl
>(Call
.getDecl())->getThisType();
747 const MemRegion
*OtherSmartPtrRegion
= OC
->getArgSVal(0).getAsRegion();
748 // In case of 'nullptr' or '0' assigned
749 if (!OtherSmartPtrRegion
) {
750 bool AssignedNull
= Call
.getArgSVal(0).isZeroConstant();
753 auto NullVal
= C
.getSValBuilder().makeNullWithType(ThisType
);
754 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, NullVal
);
755 C
.addTransition(State
, C
.getNoteTag([ThisRegion
](PathSensitiveBugReport
&BR
,
756 llvm::raw_ostream
&OS
) {
757 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
758 !BR
.isInteresting(ThisRegion
))
760 OS
<< "Smart pointer";
761 checkAndPrettyPrintRegion(OS
, ThisRegion
);
762 OS
<< " is assigned to null";
767 return updateMovedSmartPointers(C
, ThisRegion
, OtherSmartPtrRegion
, Call
);
770 bool SmartPtrModeling::handleMoveCtr(const CallEvent
&Call
, CheckerContext
&C
,
771 const MemRegion
*ThisRegion
) const {
772 const auto *OtherSmartPtrRegion
= Call
.getArgSVal(0).getAsRegion();
773 if (!OtherSmartPtrRegion
)
776 return updateMovedSmartPointers(C
, ThisRegion
, OtherSmartPtrRegion
, Call
);
779 bool SmartPtrModeling::updateMovedSmartPointers(
780 CheckerContext
&C
, const MemRegion
*ThisRegion
,
781 const MemRegion
*OtherSmartPtrRegion
, const CallEvent
&Call
) const {
782 ProgramStateRef State
= C
.getState();
783 QualType ThisType
= cast
<CXXMethodDecl
>(Call
.getDecl())->getThisType();
784 const auto *OtherInnerPtr
= State
->get
<TrackedRegionMap
>(OtherSmartPtrRegion
);
786 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, *OtherInnerPtr
);
788 auto NullVal
= C
.getSValBuilder().makeNullWithType(ThisType
);
789 State
= State
->set
<TrackedRegionMap
>(OtherSmartPtrRegion
, NullVal
);
790 bool IsArgValNull
= OtherInnerPtr
->isZeroConstant();
794 C
.getNoteTag([ThisRegion
, OtherSmartPtrRegion
, IsArgValNull
](
795 PathSensitiveBugReport
&BR
, llvm::raw_ostream
&OS
) {
796 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType())
798 if (BR
.isInteresting(OtherSmartPtrRegion
)) {
799 OS
<< "Smart pointer";
800 checkAndPrettyPrintRegion(OS
, OtherSmartPtrRegion
);
801 OS
<< " is null after being moved to";
802 checkAndPrettyPrintRegion(OS
, ThisRegion
);
804 if (BR
.isInteresting(ThisRegion
) && IsArgValNull
) {
805 OS
<< "A null pointer value is moved to";
806 checkAndPrettyPrintRegion(OS
, ThisRegion
);
807 BR
.markInteresting(OtherSmartPtrRegion
);
812 // In case we dont know anything about value we are moving from
813 // remove the entry from map for which smart pointer got moved to.
814 // For unique_ptr<A>, Ty will be 'A*'.
815 auto NullVal
= C
.getSValBuilder().makeNullWithType(ThisType
);
816 State
= State
->remove
<TrackedRegionMap
>(ThisRegion
);
817 State
= State
->set
<TrackedRegionMap
>(OtherSmartPtrRegion
, NullVal
);
818 C
.addTransition(State
, C
.getNoteTag([OtherSmartPtrRegion
,
819 ThisRegion
](PathSensitiveBugReport
&BR
,
820 llvm::raw_ostream
&OS
) {
821 if (&BR
.getBugType() != smartptr::getNullDereferenceBugType() ||
822 !BR
.isInteresting(OtherSmartPtrRegion
))
824 OS
<< "Smart pointer";
825 checkAndPrettyPrintRegion(OS
, OtherSmartPtrRegion
);
826 OS
<< " is null after; previous value moved to";
827 checkAndPrettyPrintRegion(OS
, ThisRegion
);
834 void SmartPtrModeling::handleBoolConversion(const CallEvent
&Call
,
835 CheckerContext
&C
) const {
836 // To model unique_ptr::operator bool
837 ProgramStateRef State
= C
.getState();
838 const Expr
*CallExpr
= Call
.getOriginExpr();
839 const MemRegion
*ThisRegion
=
840 cast
<CXXInstanceCall
>(&Call
)->getCXXThisVal().getAsRegion();
842 QualType ThisType
= cast
<CXXMethodDecl
>(Call
.getDecl())->getThisType();
844 SVal InnerPointerVal
;
845 if (const auto *InnerValPtr
= State
->get
<TrackedRegionMap
>(ThisRegion
)) {
846 InnerPointerVal
= *InnerValPtr
;
848 // In case of inner pointer SVal is not available we create
849 // conjureSymbolVal for inner pointer value.
850 auto InnerPointerType
= getInnerPointerType(Call
, C
);
851 if (InnerPointerType
.isNull())
854 const LocationContext
*LC
= C
.getLocationContext();
855 InnerPointerVal
= C
.getSValBuilder().conjureSymbolVal(
856 CallExpr
, LC
, InnerPointerType
, C
.blockCount());
857 State
= State
->set
<TrackedRegionMap
>(ThisRegion
, InnerPointerVal
);
860 if (State
->isNull(InnerPointerVal
).isConstrainedTrue()) {
861 State
= State
->BindExpr(CallExpr
, C
.getLocationContext(),
862 C
.getSValBuilder().makeTruthVal(false));
864 C
.addTransition(State
);
866 } else if (State
->isNonNull(InnerPointerVal
).isConstrainedTrue()) {
867 State
= State
->BindExpr(CallExpr
, C
.getLocationContext(),
868 C
.getSValBuilder().makeTruthVal(true));
870 C
.addTransition(State
);
872 } else if (move::isMovedFrom(State
, ThisRegion
)) {
874 State
->BindExpr(CallExpr
, C
.getLocationContext(),
875 C
.getSValBuilder().makeZeroVal(Call
.getResultType())));
878 ProgramStateRef NotNullState
, NullState
;
879 std::tie(NotNullState
, NullState
) =
880 State
->assume(InnerPointerVal
.castAs
<DefinedOrUnknownSVal
>());
882 auto NullVal
= C
.getSValBuilder().makeNullWithType(ThisType
);
883 // Explicitly tracking the region as null.
884 NullState
= NullState
->set
<TrackedRegionMap
>(ThisRegion
, NullVal
);
886 NullState
= NullState
->BindExpr(CallExpr
, C
.getLocationContext(),
887 C
.getSValBuilder().makeTruthVal(false));
888 C
.addTransition(NullState
, C
.getNoteTag(
889 [ThisRegion
](PathSensitiveBugReport
&BR
,
890 llvm::raw_ostream
&OS
) {
891 OS
<< "Assuming smart pointer";
892 checkAndPrettyPrintRegion(OS
, ThisRegion
);
895 /*IsPrunable=*/true));
897 NotNullState
->BindExpr(CallExpr
, C
.getLocationContext(),
898 C
.getSValBuilder().makeTruthVal(true));
902 [ThisRegion
](PathSensitiveBugReport
&BR
, llvm::raw_ostream
&OS
) {
903 OS
<< "Assuming smart pointer";
904 checkAndPrettyPrintRegion(OS
, ThisRegion
);
905 OS
<< " is non-null";
907 /*IsPrunable=*/true));
912 void ento::registerSmartPtrModeling(CheckerManager
&Mgr
) {
913 auto *Checker
= Mgr
.registerChecker
<SmartPtrModeling
>();
914 Checker
->ModelSmartPtrDereference
=
915 Mgr
.getAnalyzerOptions().getCheckerBooleanOption(
916 Checker
, "ModelSmartPtrDereference");
919 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager
&mgr
) {
920 const LangOptions
&LO
= mgr
.getLangOpts();