xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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/Checker.h"
24 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.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/STLExtras.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include <optional>
36 
37 using namespace clang;
38 using namespace ento;
39 
40 namespace {
41 
42 class SmartPtrModeling
43     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
44                      check::LiveSymbols> {
45 
46   bool isBoolConversionMethod(const CallEvent &Call) const;
47 
48 public:
49   // Whether the checker should model for null dereferences of smart pointers.
50   bool ModelSmartPtrDereference = false;
51   bool evalCall(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 handleSwapMethod(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,
73                                 const CallEvent &Call) const;
74   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
75   bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
76   bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
77   bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
78                   CheckerContext &C) const;
79   std::pair<SVal, ProgramStateRef> retrieveOrConjureInnerPtrVal(
80       ProgramStateRef State, const MemRegion *ThisRegion,
81       ConstCFGElementRef Elem, QualType Type, CheckerContext &C) const;
82 
83   using SmartPtrMethodHandlerFn =
84       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
85   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
86       {{CDM::CXXMethod, {"reset"}}, &SmartPtrModeling::handleReset},
87       {{CDM::CXXMethod, {"release"}}, &SmartPtrModeling::handleRelease},
88       {{CDM::CXXMethod, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
89       {{CDM::CXXMethod, {"get"}}, &SmartPtrModeling::handleGet}};
90   const CallDescription StdSwapCall{CDM::SimpleFunc, {"std", "swap"}, 2};
91   const CallDescriptionSet MakeUniqueVariants{
92       {CDM::SimpleFunc, {"std", "make_unique"}},
93       {CDM::SimpleFunc, {"std", "make_unique_for_overwrite"}}};
94 };
95 } // end of anonymous namespace
96 
97 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
98 
99 // Checks if RD has name in Names and is in std namespace
100 static bool hasStdClassWithName(const CXXRecordDecl *RD,
101                                 ArrayRef<llvm::StringLiteral> Names) {
102   if (!RD || !RD->getDeclContext()->isStdNamespace())
103     return false;
104   if (RD->getDeclName().isIdentifier())
105     return llvm::is_contained(Names, RD->getName());
106   return false;
107 }
108 
109 constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
110                                                  "weak_ptr"};
111 
112 static bool isStdSmartPtr(const CXXRecordDecl *RD) {
113   return hasStdClassWithName(RD, STD_PTR_NAMES);
114 }
115 
116 static bool isStdSmartPtr(const Expr *E) {
117   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
118 }
119 
120 // Define the inter-checker API.
121 namespace clang {
122 namespace ento {
123 namespace smartptr {
124 bool isStdSmartPtrCall(const CallEvent &Call) {
125   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
126   if (!MethodDecl || !MethodDecl->getParent())
127     return false;
128   return isStdSmartPtr(MethodDecl->getParent());
129 }
130 
131 bool isStdSmartPtr(const CXXRecordDecl *RD) {
132   if (!RD || !RD->getDeclContext()->isStdNamespace())
133     return false;
134 
135   if (RD->getDeclName().isIdentifier()) {
136     StringRef Name = RD->getName();
137     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
138   }
139   return false;
140 }
141 
142 bool isStdSmartPtr(const Expr *E) {
143   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
144 }
145 
146 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
147   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
148   return InnerPointVal &&
149          !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
150 }
151 } // namespace smartptr
152 } // namespace ento
153 } // namespace clang
154 
155 // If a region is removed all of the subregions need to be removed too.
156 static TrackedRegionMapTy
157 removeTrackedSubregions(TrackedRegionMapTy RegionMap,
158                         TrackedRegionMapTy::Factory &RegionMapFactory,
159                         const MemRegion *Region) {
160   if (!Region)
161     return RegionMap;
162   for (const auto &E : RegionMap) {
163     if (E.first->isSubRegionOf(Region))
164       RegionMap = RegionMapFactory.remove(RegionMap, E.first);
165   }
166   return RegionMap;
167 }
168 
169 static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
170                                            const MemRegion *Region,
171                                            const SVal *RegionInnerPointerVal) {
172   if (RegionInnerPointerVal) {
173     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
174   } else {
175     State = State->remove<TrackedRegionMap>(Region);
176   }
177   return State;
178 }
179 
180 static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
181   if (!RD || !RD->isInStdNamespace())
182     return {};
183 
184   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
185   if (!TSD)
186     return {};
187 
188   auto TemplateArgs = TSD->getTemplateArgs().asArray();
189   if (TemplateArgs.empty())
190     return {};
191   auto InnerValueType = TemplateArgs[0].getAsType();
192   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
193 }
194 
195 // This is for use with standalone-functions like std::make_unique,
196 // std::make_unique_for_overwrite, etc. It reads the template parameter and
197 // returns the pointer type corresponding to it,
198 static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
199                                               CheckerContext &C) {
200   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
201   if (!FD || !FD->getPrimaryTemplate())
202     return {};
203   const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
204   if (TemplateArgs.size() == 0)
205     return {};
206   auto ValueType = TemplateArgs[0].getAsType();
207   return C.getASTContext().getPointerType(ValueType.getCanonicalType());
208 }
209 
210 // Helper method to get the inner pointer type of specialized smart pointer
211 // Returns empty type if not found valid inner pointer type.
212 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
213   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
214   if (!MethodDecl || !MethodDecl->getParent())
215     return {};
216 
217   const auto *RecordDecl = MethodDecl->getParent();
218   return getInnerPointerType(C, RecordDecl);
219 }
220 
221 // Helper method to pretty print region and avoid extra spacing.
222 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
223                                       const MemRegion *Region) {
224   if (Region->canPrintPretty()) {
225     OS << " ";
226     Region->printPretty(OS);
227   }
228 }
229 
230 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
231   // TODO: Update CallDescription to support anonymous calls?
232   // TODO: Handle other methods, such as .get() or .release().
233   // But once we do, we'd need a visitor to explain null dereferences
234   // that are found via such modeling.
235   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
236   return CD && CD->getConversionType()->isBooleanType();
237 }
238 
239 constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
240 
241 static bool isStdBasicOstream(const Expr *E) {
242   const auto *RD = E->getType()->getAsCXXRecordDecl();
243   return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
244 }
245 
246 static bool isStdFunctionCall(const CallEvent &Call) {
247   return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
248 }
249 
250 static bool isStdOstreamOperatorCall(const CallEvent &Call) {
251   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
252     return false;
253   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
254   if (!FC)
255     return false;
256   const FunctionDecl *FD = FC->getDecl();
257   if (!FD->isOverloadedOperator())
258     return false;
259   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
260   if (OOK != clang::OO_LessLess)
261     return false;
262   return isStdSmartPtr(Call.getArgExpr(1)) &&
263          isStdBasicOstream(Call.getArgExpr(0));
264 }
265 
266 static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
267   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
268     return false;
269   return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
270          smartptr::isStdSmartPtr(Call.getArgExpr(1));
271 }
272 
273 bool SmartPtrModeling::evalCall(const CallEvent &Call,
274                                 CheckerContext &C) const {
275 
276   ProgramStateRef State = C.getState();
277 
278   // If any one of the arg is a unique_ptr, then
279   // we can try this function
280   if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
281     if (handleComparisionOp(Call, C))
282       return true;
283 
284   if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
285     return handleOstreamOperator(Call, C);
286 
287   if (StdSwapCall.matches(Call)) {
288     // Check the first arg, if it is of std::unique_ptr type.
289     assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
290     const Expr *FirstArg = Call.getArgExpr(0);
291     if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl()))
292       return false;
293     return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
294   }
295 
296   if (MakeUniqueVariants.contains(Call)) {
297     if (!ModelSmartPtrDereference)
298       return false;
299 
300     const std::optional<SVal> ThisRegionOpt =
301         Call.getReturnValueUnderConstruction();
302     if (!ThisRegionOpt)
303       return false;
304 
305     const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
306         Call.getCFGElementRef(), C.getLocationContext(),
307         getPointerTypeFromTemplateArg(Call, C), C.blockCount());
308 
309     const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
310     State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
311     State = State->assume(PtrVal, true);
312 
313     // TODO: ExprEngine should do this for us.
314     // For a bit more context:
315     // 1) Why do we need this? Since we are modelling a "function"
316     // that returns a constructed object we need to store this information in
317     // the program state.
318     //
319     // 2) Why does this work?
320     // `updateObjectsUnderConstruction` does exactly as it sounds.
321     //
322     // 3) How should it look like when moved to the Engine?
323     // It would be nice if we can just
324     // pretend we don't need to know about this - ie, completely automatic work.
325     // However, realistically speaking, I think we would need to "signal" the
326     // ExprEngine evalCall handler that we are constructing an object with this
327     // function call (constructors obviously construct, hence can be
328     // automatically deduced).
329     auto &Engine = State->getStateManager().getOwningEngine();
330     State = Engine.updateObjectsUnderConstruction(
331         *ThisRegionOpt, nullptr, State, C.getLocationContext(),
332         Call.getConstructionContext(), {});
333 
334     // We don't leave a note here since it is guaranteed the
335     // unique_ptr from this call is non-null (hence is safe to de-reference).
336     C.addTransition(State);
337     return true;
338   }
339 
340   if (!smartptr::isStdSmartPtrCall(Call))
341     return false;
342 
343   if (isBoolConversionMethod(Call)) {
344     const MemRegion *ThisR =
345         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
346 
347     if (ModelSmartPtrDereference) {
348       // The check for the region is moved is duplicated in handleBoolOperation
349       // method.
350       // FIXME: Once we model std::move for smart pointers clean up this and use
351       // that modeling.
352       handleBoolConversion(Call, C);
353       return true;
354     } else {
355       if (!move::isMovedFrom(State, ThisR)) {
356         // TODO: Model this case as well. At least, avoid invalidation of
357         // globals.
358         return false;
359       }
360 
361       // TODO: Add a note to bug reports describing this decision.
362       C.addTransition(State->BindExpr(
363           Call.getOriginExpr(), C.getLocationContext(),
364           C.getSValBuilder().makeZeroVal(Call.getResultType())));
365 
366       return true;
367     }
368   }
369 
370   if (!ModelSmartPtrDereference)
371     return false;
372 
373   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
374     if (CC->getDecl()->isCopyConstructor())
375       return false;
376 
377     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
378     if (!ThisRegion)
379       return false;
380 
381     QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
382 
383     if (CC->getDecl()->isMoveConstructor())
384       return handleMoveCtr(Call, C, ThisRegion);
385 
386     if (Call.getNumArgs() == 0) {
387       auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
388       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
389 
390       C.addTransition(
391           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
392                                            llvm::raw_ostream &OS) {
393             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
394                 !BR.isInteresting(ThisRegion))
395               return;
396             OS << "Default constructed smart pointer";
397             checkAndPrettyPrintRegion(OS, ThisRegion);
398             OS << " is null";
399           }));
400     } else {
401       const auto *TrackingExpr = Call.getArgExpr(0);
402       assert(TrackingExpr->getType()->isPointerType() &&
403              "Adding a non pointer value to TrackedRegionMap");
404       auto ArgVal = Call.getArgSVal(0);
405       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
406 
407       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
408                                            ArgVal](PathSensitiveBugReport &BR,
409                                                    llvm::raw_ostream &OS) {
410         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
411             !BR.isInteresting(ThisRegion))
412           return;
413         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
414         OS << "Smart pointer";
415         checkAndPrettyPrintRegion(OS, ThisRegion);
416         if (ArgVal.isZeroConstant())
417           OS << " is constructed using a null value";
418         else
419           OS << " is constructed";
420       }));
421     }
422     return true;
423   }
424 
425   if (handleAssignOp(Call, C))
426     return true;
427 
428   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
429   if (!Handler)
430     return false;
431   (this->**Handler)(Call, C);
432 
433   return C.isDifferent();
434 }
435 
436 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
437     ProgramStateRef State, const MemRegion *ThisRegion, ConstCFGElementRef Elem,
438     QualType Type, CheckerContext &C) const {
439   const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
440   if (Ptr)
441     return {*Ptr, State};
442   auto Val = C.getSValBuilder().conjureSymbolVal(Elem, C.getLocationContext(),
443                                                  Type, C.blockCount());
444   State = State->set<TrackedRegionMap>(ThisRegion, Val);
445   return {Val, State};
446 }
447 
448 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
449                                            CheckerContext &C) const {
450   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
451   if (!FC)
452     return false;
453   const FunctionDecl *FD = FC->getDecl();
454   if (!FD->isOverloadedOperator())
455     return false;
456   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
457   if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
458         OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
459         OOK == OO_Spaceship))
460     return false;
461 
462   // There are some special cases about which we can infer about
463   // the resulting answer.
464   // For reference, there is a discussion at https://reviews.llvm.org/D104616.
465   // Also, the cppreference page is good to look at
466   // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
467 
468   auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
469                                 ConstCFGElementRef Elem,
470                                 SVal S) -> std::pair<SVal, ProgramStateRef> {
471     if (S.isZeroConstant()) {
472       return {S, State};
473     }
474     const MemRegion *Reg = S.getAsRegion();
475     assert(Reg &&
476            "this pointer of std::unique_ptr should be obtainable as MemRegion");
477     QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
478     return retrieveOrConjureInnerPtrVal(State, Reg, Elem, Type, C);
479   };
480 
481   SVal First = Call.getArgSVal(0);
482   SVal Second = Call.getArgSVal(1);
483   const auto *FirstExpr = Call.getArgExpr(0);
484   const auto *SecondExpr = Call.getArgExpr(1);
485 
486   const auto *ResultExpr = Call.getOriginExpr();
487   const auto *LCtx = C.getLocationContext();
488   auto &Bldr = C.getSValBuilder();
489   ProgramStateRef State = C.getState();
490 
491   SVal FirstPtrVal, SecondPtrVal;
492   std::tie(FirstPtrVal, State) =
493       makeSValFor(State, FirstExpr, Call.getCFGElementRef(), First);
494   std::tie(SecondPtrVal, State) =
495       makeSValFor(State, SecondExpr, Call.getCFGElementRef(), Second);
496   BinaryOperatorKind BOK =
497       operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
498   auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499                                Call.getResultType());
500 
501   if (OOK != OO_Spaceship) {
502     ProgramStateRef TrueState, FalseState;
503     std::tie(TrueState, FalseState) =
504         State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505     if (TrueState)
506       C.addTransition(
507           TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
508     if (FalseState)
509       C.addTransition(
510           FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
511   } else {
512     C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
513   }
514   return true;
515 }
516 
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. :)
526 
527   ProgramStateRef State = C.getState();
528   const auto StreamVal = Call.getArgSVal(0);
529   const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530   if (!StreamThisRegion)
531     return false;
532   State =
533       State->invalidateRegions({StreamThisRegion}, Call.getCFGElementRef(),
534                                C.blockCount(), C.getLocationContext(), false);
535   State =
536       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
537   C.addTransition(State);
538   return true;
539 }
540 
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);
549 
550     if (IsRegDead)
551       State = State->remove<TrackedRegionMap>(Region);
552   }
553   C.addTransition(State);
554 }
555 
556 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
557                                   const char *NL, const char *Sep) const {
558   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
559 
560   if (!RS.isEmpty()) {
561     Out << Sep << "Smart ptr regions :" << NL;
562     for (auto I : RS) {
563       I.first->dumpToStream(Out);
564       if (smartptr::isNullSmartPtr(State, I.first))
565         Out << ": Null";
566       else
567         Out << ": Non Null";
568       Out << NL;
569     }
570   }
571 }
572 
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);
585 }
586 
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()) {
593       SR.markLive(Sym);
594     }
595   }
596 }
597 
598 void SmartPtrModeling::handleReset(const CallEvent &Call,
599                                    CheckerContext &C) const {
600   ProgramStateRef State = C.getState();
601   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
602   if (!IC)
603     return;
604 
605   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
606   if (!ThisRegion)
607     return;
608 
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);
613   C.addTransition(
614       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
615                                                      llvm::raw_ostream &OS) {
616         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
617             !BR.isInteresting(ThisRegion))
618           return;
619         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
620         OS << "Smart pointer";
621         checkAndPrettyPrintRegion(OS, ThisRegion);
622         OS << " reset using a null value";
623       }));
624   // TODO: Make sure to ivalidate the region in the Store if we don't have
625   // time to model all methods.
626 }
627 
628 void SmartPtrModeling::handleRelease(const CallEvent &Call,
629                                      CheckerContext &C) const {
630   ProgramStateRef State = C.getState();
631   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
632   if (!IC)
633     return;
634 
635   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
636   if (!ThisRegion)
637     return;
638 
639   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
640 
641   if (InnerPointVal) {
642     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
643                             *InnerPointVal);
644   }
645 
646   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
647   auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType);
648   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
649 
650   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
651                                                    llvm::raw_ostream &OS) {
652     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
653         !BR.isInteresting(ThisRegion))
654       return;
655 
656     OS << "Smart pointer";
657     checkAndPrettyPrintRegion(OS, ThisRegion);
658     OS << " is released and set to null";
659   }));
660   // TODO: Add support to enable MallocChecker to start tracking the raw
661   // pointer.
662 }
663 
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);
668   if (!IC)
669     return;
670 
671   auto State = C.getState();
672   handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
673 }
674 
675 bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
676                                   SVal Second, CheckerContext &C) const {
677   const MemRegion *FirstThisRegion = First.getAsRegion();
678   if (!FirstThisRegion)
679     return false;
680   const MemRegion *SecondThisRegion = Second.getAsRegion();
681   if (!SecondThisRegion)
682     return false;
683 
684   const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
685   const auto *SecondInnerPtrVal =
686       State->get<TrackedRegionMap>(SecondThisRegion);
687 
688   State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
689   State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
690 
691   C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
692                                           PathSensitiveBugReport &BR,
693                                           llvm::raw_ostream &OS) {
694     if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
695       return;
696     if (BR.isInteresting(FirstThisRegion) &&
697         !BR.isInteresting(SecondThisRegion)) {
698       BR.markInteresting(SecondThisRegion);
699       BR.markNotInteresting(FirstThisRegion);
700     }
701     if (BR.isInteresting(SecondThisRegion) &&
702         !BR.isInteresting(FirstThisRegion)) {
703       BR.markInteresting(FirstThisRegion);
704       BR.markNotInteresting(SecondThisRegion);
705     }
706     // TODO: We need to emit some note here probably!!
707   }));
708 
709   return true;
710 }
711 
712 void SmartPtrModeling::handleGet(const CallEvent &Call,
713                                  CheckerContext &C) const {
714   ProgramStateRef State = C.getState();
715   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
716   if (!IC)
717     return;
718 
719   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
720   if (!ThisRegion)
721     return;
722 
723   SVal InnerPointerVal;
724   std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
725       State, ThisRegion, Call.getCFGElementRef(), Call.getResultType(), C);
726   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
727                           InnerPointerVal);
728   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
729   C.addTransition(State);
730 }
731 
732 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
733                                       CheckerContext &C) const {
734   ProgramStateRef State = C.getState();
735   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
736   if (!OC)
737     return false;
738   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
739   if (OOK != OO_Equal)
740     return false;
741   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
742   if (!ThisRegion)
743     return false;
744 
745   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
746 
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();
751     if (!AssignedNull)
752       return false;
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))
759         return;
760       OS << "Smart pointer";
761       checkAndPrettyPrintRegion(OS, ThisRegion);
762       OS << " is assigned to null";
763     }));
764     return true;
765   }
766 
767   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
768 }
769 
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)
774     return false;
775 
776   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
777 }
778 
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);
785   if (OtherInnerPtr) {
786     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
787 
788     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
789     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
790     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
791 
792     C.addTransition(
793         State,
794         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
795                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
796           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
797             return;
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);
803           }
804           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
805             OS << "A null pointer value is moved to";
806             checkAndPrettyPrintRegion(OS, ThisRegion);
807             BR.markInteresting(OtherSmartPtrRegion);
808           }
809         }));
810     return true;
811   } else {
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))
823         return;
824       OS << "Smart pointer";
825       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
826       OS << " is null after; previous value moved to";
827       checkAndPrettyPrintRegion(OS, ThisRegion);
828     }));
829     return true;
830   }
831   return false;
832 }
833 
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();
841 
842   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
843 
844   SVal InnerPointerVal;
845   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
846     InnerPointerVal = *InnerValPtr;
847   } else {
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())
852       return;
853 
854     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
855         Call, InnerPointerType, C.blockCount());
856     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
857   }
858 
859   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
860     State = State->BindExpr(CallExpr, C.getLocationContext(),
861                             C.getSValBuilder().makeTruthVal(false));
862 
863     C.addTransition(State);
864     return;
865   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
866     State = State->BindExpr(CallExpr, C.getLocationContext(),
867                             C.getSValBuilder().makeTruthVal(true));
868 
869     C.addTransition(State);
870     return;
871   } else if (move::isMovedFrom(State, ThisRegion)) {
872     C.addTransition(
873         State->BindExpr(CallExpr, C.getLocationContext(),
874                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
875     return;
876   } else {
877     ProgramStateRef NotNullState, NullState;
878     std::tie(NotNullState, NullState) =
879         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
880 
881     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
882     // Explicitly tracking the region as null.
883     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
884 
885     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
886                                     C.getSValBuilder().makeTruthVal(false));
887     C.addTransition(NullState, C.getNoteTag(
888                                    [ThisRegion](PathSensitiveBugReport &BR,
889                                                 llvm::raw_ostream &OS) {
890                                      OS << "Assuming smart pointer";
891                                      checkAndPrettyPrintRegion(OS, ThisRegion);
892                                      OS << " is null";
893                                    },
894                                    /*IsPrunable=*/true));
895     NotNullState =
896         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
897                                C.getSValBuilder().makeTruthVal(true));
898     C.addTransition(
899         NotNullState,
900         C.getNoteTag(
901             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
902               OS << "Assuming smart pointer";
903               checkAndPrettyPrintRegion(OS, ThisRegion);
904               OS << " is non-null";
905             },
906             /*IsPrunable=*/true));
907     return;
908   }
909 }
910 
911 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
912   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
913   Checker->ModelSmartPtrDereference =
914       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
915           Checker, "ModelSmartPtrDereference");
916 }
917 
918 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
919   const LangOptions &LO = mgr.getLangOpts();
920   return LO.CPlusPlus;
921 }
922