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