xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp (revision 13fe78212fe7fa3a4491ffbbac6f0c58cb1e0573)
1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a checker that models various aspects of
10 // C++ smart pointer behavior.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Move.h"
15 #include "SmartPtr.h"
16 
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/CallEvent.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
32 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
33 #include "llvm/ADT/StringMap.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include <string>
36 
37 using namespace clang;
38 using namespace ento;
39 
40 namespace {
41 class SmartPtrModeling
42     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
43                      check::LiveSymbols> {
44 
45   bool isBoolConversionMethod(const CallEvent &Call) const;
46 
47 public:
48   // Whether the checker should model for null dereferences of smart pointers.
49   DefaultBool ModelSmartPtrDereference;
50   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
51   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
52   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
53   ProgramStateRef
54   checkRegionChanges(ProgramStateRef State,
55                      const InvalidatedSymbols *Invalidated,
56                      ArrayRef<const MemRegion *> ExplicitRegions,
57                      ArrayRef<const MemRegion *> Regions,
58                      const LocationContext *LCtx, const CallEvent *Call) const;
59   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
60                   const char *Sep) const override;
61   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
62 
63 private:
64   void handleReset(const CallEvent &Call, CheckerContext &C) const;
65   void handleRelease(const CallEvent &Call, CheckerContext &C) const;
66   void handleSwap(const CallEvent &Call, CheckerContext &C) const;
67   void handleGet(const CallEvent &Call, CheckerContext &C) const;
68   bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
69   bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
70                      const MemRegion *ThisRegion) const;
71   bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
72                                 const MemRegion *OtherSmartPtrRegion) const;
73   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
74   bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
75   bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
76   std::pair<SVal, ProgramStateRef>
77   retrieveOrConjureInnerPtrVal(ProgramStateRef State,
78                                const MemRegion *ThisRegion, const Expr *E,
79                                QualType Type, CheckerContext &C) const;
80 
81   using SmartPtrMethodHandlerFn =
82       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
83   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
84       {{"reset"}, &SmartPtrModeling::handleReset},
85       {{"release"}, &SmartPtrModeling::handleRelease},
86       {{"swap", 1}, &SmartPtrModeling::handleSwap},
87       {{"get"}, &SmartPtrModeling::handleGet}};
88 };
89 } // end of anonymous namespace
90 
91 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
92 
93 // Checks if RD has name in Names and is in std namespace
94 static bool hasStdClassWithName(const CXXRecordDecl *RD,
95                                 ArrayRef<llvm::StringLiteral> Names) {
96   if (!RD || !RD->getDeclContext()->isStdNamespace())
97     return false;
98   if (RD->getDeclName().isIdentifier()) {
99     StringRef Name = RD->getName();
100     return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool {
101       return Name == GivenName;
102     });
103   }
104   return false;
105 }
106 
107 constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
108                                                  "weak_ptr"};
109 
110 static bool isStdSmartPtr(const CXXRecordDecl *RD) {
111   return hasStdClassWithName(RD, STD_PTR_NAMES);
112 }
113 
114 static bool isStdSmartPtr(const Expr *E) {
115   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
116 }
117 
118 // Define the inter-checker API.
119 namespace clang {
120 namespace ento {
121 namespace smartptr {
122 bool isStdSmartPtrCall(const CallEvent &Call) {
123   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
124   if (!MethodDecl || !MethodDecl->getParent())
125     return false;
126   return isStdSmartPtr(MethodDecl->getParent());
127 }
128 
129 bool isStdSmartPtr(const CXXRecordDecl *RD) {
130   if (!RD || !RD->getDeclContext()->isStdNamespace())
131     return false;
132 
133   if (RD->getDeclName().isIdentifier()) {
134     StringRef Name = RD->getName();
135     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
136   }
137   return false;
138 }
139 
140 bool isStdSmartPtr(const Expr *E) {
141   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
142 }
143 
144 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
145   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
146   return InnerPointVal &&
147          !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
148 }
149 } // namespace smartptr
150 } // namespace ento
151 } // namespace clang
152 
153 // If a region is removed all of the subregions need to be removed too.
154 static TrackedRegionMapTy
155 removeTrackedSubregions(TrackedRegionMapTy RegionMap,
156                         TrackedRegionMapTy::Factory &RegionMapFactory,
157                         const MemRegion *Region) {
158   if (!Region)
159     return RegionMap;
160   for (const auto &E : RegionMap) {
161     if (E.first->isSubRegionOf(Region))
162       RegionMap = RegionMapFactory.remove(RegionMap, E.first);
163   }
164   return RegionMap;
165 }
166 
167 static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
168                                            const MemRegion *Region,
169                                            const SVal *RegionInnerPointerVal) {
170   if (RegionInnerPointerVal) {
171     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
172   } else {
173     State = State->remove<TrackedRegionMap>(Region);
174   }
175   return State;
176 }
177 
178 static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
179   if (!RD || !RD->isInStdNamespace())
180     return {};
181 
182   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
183   if (!TSD)
184     return {};
185 
186   auto TemplateArgs = TSD->getTemplateArgs().asArray();
187   if (TemplateArgs.size() == 0)
188     return {};
189   auto InnerValueType = TemplateArgs[0].getAsType();
190   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
191 }
192 
193 // Helper method to get the inner pointer type of specialized smart pointer
194 // Returns empty type if not found valid inner pointer type.
195 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
196   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
197   if (!MethodDecl || !MethodDecl->getParent())
198     return {};
199 
200   const auto *RecordDecl = MethodDecl->getParent();
201   return getInnerPointerType(C, RecordDecl);
202 }
203 
204 // Helper method to pretty print region and avoid extra spacing.
205 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
206                                       const MemRegion *Region) {
207   if (Region->canPrintPretty()) {
208     OS << " ";
209     Region->printPretty(OS);
210   }
211 }
212 
213 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
214   // TODO: Update CallDescription to support anonymous calls?
215   // TODO: Handle other methods, such as .get() or .release().
216   // But once we do, we'd need a visitor to explain null dereferences
217   // that are found via such modeling.
218   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
219   return CD && CD->getConversionType()->isBooleanType();
220 }
221 
222 constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
223 
224 bool isStdBasicOstream(const Expr *E) {
225   const auto *RD = E->getType()->getAsCXXRecordDecl();
226   return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
227 }
228 
229 bool isStdOstreamOperatorCall(const CallEvent &Call) {
230   if (Call.getNumArgs() != 2 ||
231       !Call.getDecl()->getDeclContext()->isStdNamespace())
232     return false;
233   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
234   if (!FC)
235     return false;
236   const FunctionDecl *FD = FC->getDecl();
237   if (!FD->isOverloadedOperator())
238     return false;
239   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
240   if (OOK != clang::OO_LessLess)
241     return false;
242   return isStdSmartPtr(Call.getArgExpr(1)) &&
243          isStdBasicOstream(Call.getArgExpr(0));
244 }
245 
246 bool SmartPtrModeling::evalCall(const CallEvent &Call,
247                                 CheckerContext &C) const {
248   ProgramStateRef State = C.getState();
249 
250   // If any one of the arg is a unique_ptr, then
251   // we can try this function
252   if (Call.getNumArgs() == 2 &&
253       Call.getDecl()->getDeclContext()->isStdNamespace())
254     if (smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
255         smartptr::isStdSmartPtr(Call.getArgExpr(1)))
256       if (handleComparisionOp(Call, C))
257         return true;
258 
259   if (isStdOstreamOperatorCall(Call))
260     return handleOstreamOperator(Call, C);
261 
262   if (!smartptr::isStdSmartPtrCall(Call))
263     return false;
264 
265   if (isBoolConversionMethod(Call)) {
266     const MemRegion *ThisR =
267         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
268 
269     if (ModelSmartPtrDereference) {
270       // The check for the region is moved is duplicated in handleBoolOperation
271       // method.
272       // FIXME: Once we model std::move for smart pointers clean up this and use
273       // that modeling.
274       handleBoolConversion(Call, C);
275       return true;
276     } else {
277       if (!move::isMovedFrom(State, ThisR)) {
278         // TODO: Model this case as well. At least, avoid invalidation of
279         // globals.
280         return false;
281       }
282 
283       // TODO: Add a note to bug reports describing this decision.
284       C.addTransition(State->BindExpr(
285           Call.getOriginExpr(), C.getLocationContext(),
286           C.getSValBuilder().makeZeroVal(Call.getResultType())));
287 
288       return true;
289     }
290   }
291 
292   if (!ModelSmartPtrDereference)
293     return false;
294 
295   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
296     if (CC->getDecl()->isCopyConstructor())
297       return false;
298 
299     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
300     if (!ThisRegion)
301       return false;
302 
303     if (CC->getDecl()->isMoveConstructor())
304       return handleMoveCtr(Call, C, ThisRegion);
305 
306     if (Call.getNumArgs() == 0) {
307       auto NullVal = C.getSValBuilder().makeNull();
308       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
309 
310       C.addTransition(
311           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
312                                            llvm::raw_ostream &OS) {
313             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
314                 !BR.isInteresting(ThisRegion))
315               return;
316             OS << "Default constructed smart pointer";
317             checkAndPrettyPrintRegion(OS, ThisRegion);
318             OS << " is null";
319           }));
320     } else {
321       const auto *TrackingExpr = Call.getArgExpr(0);
322       assert(TrackingExpr->getType()->isPointerType() &&
323              "Adding a non pointer value to TrackedRegionMap");
324       auto ArgVal = Call.getArgSVal(0);
325       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
326 
327       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
328                                            ArgVal](PathSensitiveBugReport &BR,
329                                                    llvm::raw_ostream &OS) {
330         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
331             !BR.isInteresting(ThisRegion))
332           return;
333         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
334         OS << "Smart pointer";
335         checkAndPrettyPrintRegion(OS, ThisRegion);
336         if (ArgVal.isZeroConstant())
337           OS << " is constructed using a null value";
338         else
339           OS << " is constructed";
340       }));
341     }
342     return true;
343   }
344 
345   if (handleAssignOp(Call, C))
346     return true;
347 
348   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
349   if (!Handler)
350     return false;
351   (this->**Handler)(Call, C);
352 
353   return C.isDifferent();
354 }
355 
356 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
357     ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
358     QualType Type, CheckerContext &C) const {
359   const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
360   if (Ptr)
361     return {*Ptr, State};
362   auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
363                                                  Type, C.blockCount());
364   State = State->set<TrackedRegionMap>(ThisRegion, Val);
365   return {Val, State};
366 }
367 
368 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
369                                            CheckerContext &C) const {
370   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
371   if (!FC)
372     return false;
373   const FunctionDecl *FD = FC->getDecl();
374   if (!FD->isOverloadedOperator())
375     return false;
376   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
377   if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
378         OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
379         OOK == OO_Spaceship))
380     return false;
381 
382   // There are some special cases about which we can infer about
383   // the resulting answer.
384   // For reference, there is a discussion at https://reviews.llvm.org/D104616.
385   // Also, the cppreference page is good to look at
386   // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
387 
388   auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
389                                 SVal S) -> std::pair<SVal, ProgramStateRef> {
390     if (S.isZeroConstant()) {
391       return {S, State};
392     }
393     const MemRegion *Reg = S.getAsRegion();
394     assert(Reg &&
395            "this pointer of std::unique_ptr should be obtainable as MemRegion");
396     QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
397     return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
398   };
399 
400   SVal First = Call.getArgSVal(0);
401   SVal Second = Call.getArgSVal(1);
402   const auto *FirstExpr = Call.getArgExpr(0);
403   const auto *SecondExpr = Call.getArgExpr(1);
404 
405   const auto *ResultExpr = Call.getOriginExpr();
406   const auto *LCtx = C.getLocationContext();
407   auto &Bldr = C.getSValBuilder();
408   ProgramStateRef State = C.getState();
409 
410   SVal FirstPtrVal, SecondPtrVal;
411   std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
412   std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
413   BinaryOperatorKind BOK =
414       operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
415   auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
416                                Call.getResultType());
417 
418   if (OOK != OO_Spaceship) {
419     ProgramStateRef TrueState, FalseState;
420     std::tie(TrueState, FalseState) =
421         State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
422     if (TrueState)
423       C.addTransition(
424           TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
425     if (FalseState)
426       C.addTransition(
427           FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
428   } else {
429     C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
430   }
431   return true;
432 }
433 
434 bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
435                                              CheckerContext &C) const {
436   // operator<< does not modify the smart pointer.
437   // And we don't really have much of modelling of basic_ostream.
438   // So, we are better off:
439   // 1) Invalidating the mem-region of the ostream object at hand.
440   // 2) Setting the SVal of the basic_ostream as the return value.
441   // Not very satisfying, but it gets the job done, and is better
442   // than the default handling. :)
443 
444   ProgramStateRef State = C.getState();
445   const auto StreamVal = Call.getArgSVal(0);
446   const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
447   if (!StreamThisRegion)
448     return false;
449   State =
450       State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
451                                C.blockCount(), C.getLocationContext(), false);
452   State =
453       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
454   C.addTransition(State);
455   return true;
456 }
457 
458 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
459                                         CheckerContext &C) const {
460   ProgramStateRef State = C.getState();
461   // Clean up dead regions from the region map.
462   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
463   for (auto E : TrackedRegions) {
464     const MemRegion *Region = E.first;
465     bool IsRegDead = !SymReaper.isLiveRegion(Region);
466 
467     if (IsRegDead)
468       State = State->remove<TrackedRegionMap>(Region);
469   }
470   C.addTransition(State);
471 }
472 
473 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
474                                   const char *NL, const char *Sep) const {
475   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
476 
477   if (!RS.isEmpty()) {
478     Out << Sep << "Smart ptr regions :" << NL;
479     for (auto I : RS) {
480       I.first->dumpToStream(Out);
481       if (smartptr::isNullSmartPtr(State, I.first))
482         Out << ": Null";
483       else
484         Out << ": Non Null";
485       Out << NL;
486     }
487   }
488 }
489 
490 ProgramStateRef SmartPtrModeling::checkRegionChanges(
491     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
492     ArrayRef<const MemRegion *> ExplicitRegions,
493     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
494     const CallEvent *Call) const {
495   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
496   TrackedRegionMapTy::Factory &RegionMapFactory =
497       State->get_context<TrackedRegionMap>();
498   for (const auto *Region : Regions)
499     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
500                                         Region->getBaseRegion());
501   return State->set<TrackedRegionMap>(RegionMap);
502 }
503 
504 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
505                                         SymbolReaper &SR) const {
506   // Marking tracked symbols alive
507   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
508   for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
509     SVal Val = I->second;
510     for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
511       SR.markLive(*si);
512     }
513   }
514 }
515 
516 void SmartPtrModeling::handleReset(const CallEvent &Call,
517                                    CheckerContext &C) const {
518   ProgramStateRef State = C.getState();
519   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
520   if (!IC)
521     return;
522 
523   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
524   if (!ThisRegion)
525     return;
526 
527   assert(Call.getArgExpr(0)->getType()->isPointerType() &&
528          "Adding a non pointer value to TrackedRegionMap");
529   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
530   const auto *TrackingExpr = Call.getArgExpr(0);
531   C.addTransition(
532       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
533                                                      llvm::raw_ostream &OS) {
534         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
535             !BR.isInteresting(ThisRegion))
536           return;
537         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
538         OS << "Smart pointer";
539         checkAndPrettyPrintRegion(OS, ThisRegion);
540         OS << " reset using a null value";
541       }));
542   // TODO: Make sure to ivalidate the region in the Store if we don't have
543   // time to model all methods.
544 }
545 
546 void SmartPtrModeling::handleRelease(const CallEvent &Call,
547                                      CheckerContext &C) const {
548   ProgramStateRef State = C.getState();
549   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
550   if (!IC)
551     return;
552 
553   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
554   if (!ThisRegion)
555     return;
556 
557   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
558 
559   if (InnerPointVal) {
560     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
561                             *InnerPointVal);
562   }
563 
564   auto ValueToUpdate = C.getSValBuilder().makeNull();
565   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
566 
567   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
568                                                    llvm::raw_ostream &OS) {
569     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
570         !BR.isInteresting(ThisRegion))
571       return;
572 
573     OS << "Smart pointer";
574     checkAndPrettyPrintRegion(OS, ThisRegion);
575     OS << " is released and set to null";
576   }));
577   // TODO: Add support to enable MallocChecker to start tracking the raw
578   // pointer.
579 }
580 
581 void SmartPtrModeling::handleSwap(const CallEvent &Call,
582                                   CheckerContext &C) const {
583   // To model unique_ptr::swap() method.
584   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
585   if (!IC)
586     return;
587 
588   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
589   if (!ThisRegion)
590     return;
591 
592   const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
593   if (!ArgRegion)
594     return;
595 
596   auto State = C.getState();
597   const auto *ThisRegionInnerPointerVal =
598       State->get<TrackedRegionMap>(ThisRegion);
599   const auto *ArgRegionInnerPointerVal =
600       State->get<TrackedRegionMap>(ArgRegion);
601 
602   // Swap the tracked region values.
603   State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
604   State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
605 
606   C.addTransition(
607       State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
608                                                   llvm::raw_ostream &OS) {
609         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
610             !BR.isInteresting(ThisRegion))
611           return;
612         BR.markInteresting(ArgRegion);
613         OS << "Swapped null smart pointer";
614         checkAndPrettyPrintRegion(OS, ArgRegion);
615         OS << " with smart pointer";
616         checkAndPrettyPrintRegion(OS, ThisRegion);
617       }));
618 }
619 
620 void SmartPtrModeling::handleGet(const CallEvent &Call,
621                                  CheckerContext &C) const {
622   ProgramStateRef State = C.getState();
623   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
624   if (!IC)
625     return;
626 
627   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
628   if (!ThisRegion)
629     return;
630 
631   SVal InnerPointerVal;
632   std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
633       State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
634   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
635                           InnerPointerVal);
636   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
637   C.addTransition(State);
638 }
639 
640 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
641                                       CheckerContext &C) const {
642   ProgramStateRef State = C.getState();
643   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
644   if (!OC)
645     return false;
646   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
647   if (OOK != OO_Equal)
648     return false;
649   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
650   if (!ThisRegion)
651     return false;
652 
653   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
654   // In case of 'nullptr' or '0' assigned
655   if (!OtherSmartPtrRegion) {
656     bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
657     if (!AssignedNull)
658       return false;
659     auto NullVal = C.getSValBuilder().makeNull();
660     State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
661     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
662                                                      llvm::raw_ostream &OS) {
663       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
664           !BR.isInteresting(ThisRegion))
665         return;
666       OS << "Smart pointer";
667       checkAndPrettyPrintRegion(OS, ThisRegion);
668       OS << " is assigned to null";
669     }));
670     return true;
671   }
672 
673   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
674 }
675 
676 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
677                                      const MemRegion *ThisRegion) const {
678   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
679   if (!OtherSmartPtrRegion)
680     return false;
681 
682   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
683 }
684 
685 bool SmartPtrModeling::updateMovedSmartPointers(
686     CheckerContext &C, const MemRegion *ThisRegion,
687     const MemRegion *OtherSmartPtrRegion) const {
688   ProgramStateRef State = C.getState();
689   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
690   if (OtherInnerPtr) {
691     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
692     auto NullVal = C.getSValBuilder().makeNull();
693     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
694     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
695 
696     C.addTransition(
697         State,
698         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
699                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
700           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
701             return;
702           if (BR.isInteresting(OtherSmartPtrRegion)) {
703             OS << "Smart pointer";
704             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
705             OS << " is null after being moved to";
706             checkAndPrettyPrintRegion(OS, ThisRegion);
707           }
708           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
709             OS << "A null pointer value is moved to";
710             checkAndPrettyPrintRegion(OS, ThisRegion);
711             BR.markInteresting(OtherSmartPtrRegion);
712           }
713         }));
714     return true;
715   } else {
716     // In case we dont know anything about value we are moving from
717     // remove the entry from map for which smart pointer got moved to.
718     auto NullVal = C.getSValBuilder().makeNull();
719     State = State->remove<TrackedRegionMap>(ThisRegion);
720     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
721     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
722                                          ThisRegion](PathSensitiveBugReport &BR,
723                                                      llvm::raw_ostream &OS) {
724       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
725           !BR.isInteresting(OtherSmartPtrRegion))
726         return;
727       OS << "Smart pointer";
728       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
729       OS << " is null after; previous value moved to";
730       checkAndPrettyPrintRegion(OS, ThisRegion);
731     }));
732     return true;
733   }
734   return false;
735 }
736 
737 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
738                                             CheckerContext &C) const {
739   // To model unique_ptr::operator bool
740   ProgramStateRef State = C.getState();
741   const Expr *CallExpr = Call.getOriginExpr();
742   const MemRegion *ThisRegion =
743       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
744 
745   SVal InnerPointerVal;
746   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
747     InnerPointerVal = *InnerValPtr;
748   } else {
749     // In case of inner pointer SVal is not available we create
750     // conjureSymbolVal for inner pointer value.
751     auto InnerPointerType = getInnerPointerType(Call, C);
752     if (InnerPointerType.isNull())
753       return;
754 
755     const LocationContext *LC = C.getLocationContext();
756     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
757         CallExpr, LC, InnerPointerType, C.blockCount());
758     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
759   }
760 
761   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
762     State = State->BindExpr(CallExpr, C.getLocationContext(),
763                             C.getSValBuilder().makeTruthVal(false));
764 
765     C.addTransition(State);
766     return;
767   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
768     State = State->BindExpr(CallExpr, C.getLocationContext(),
769                             C.getSValBuilder().makeTruthVal(true));
770 
771     C.addTransition(State);
772     return;
773   } else if (move::isMovedFrom(State, ThisRegion)) {
774     C.addTransition(
775         State->BindExpr(CallExpr, C.getLocationContext(),
776                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
777     return;
778   } else {
779     ProgramStateRef NotNullState, NullState;
780     std::tie(NotNullState, NullState) =
781         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
782 
783     auto NullVal = C.getSValBuilder().makeNull();
784     // Explicitly tracking the region as null.
785     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
786 
787     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
788                                     C.getSValBuilder().makeTruthVal(false));
789     C.addTransition(NullState, C.getNoteTag(
790                                    [ThisRegion](PathSensitiveBugReport &BR,
791                                                 llvm::raw_ostream &OS) {
792                                      OS << "Assuming smart pointer";
793                                      checkAndPrettyPrintRegion(OS, ThisRegion);
794                                      OS << " is null";
795                                    },
796                                    /*IsPrunable=*/true));
797     NotNullState =
798         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
799                                C.getSValBuilder().makeTruthVal(true));
800     C.addTransition(
801         NotNullState,
802         C.getNoteTag(
803             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
804               OS << "Assuming smart pointer";
805               checkAndPrettyPrintRegion(OS, ThisRegion);
806               OS << " is non-null";
807             },
808             /*IsPrunable=*/true));
809     return;
810   }
811 }
812 
813 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
814   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
815   Checker->ModelSmartPtrDereference =
816       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
817           Checker, "ModelSmartPtrDereference");
818 }
819 
820 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
821   const LangOptions &LO = mgr.getLangOpts();
822   return LO.CPlusPlus;
823 }
824