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