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