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