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