• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/MemRegion.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
32 #include <string>
33 
34 using namespace clang;
35 using namespace ento;
36 
37 namespace {
38 class SmartPtrModeling
39     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40                      check::LiveSymbols> {
41 
42   bool isBoolConversionMethod(const CallEvent &Call) const;
43 
44 public:
45   // Whether the checker should model for null dereferences of smart pointers.
46   DefaultBool ModelSmartPtrDereference;
47   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
48   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
49   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
50   ProgramStateRef
51   checkRegionChanges(ProgramStateRef State,
52                      const InvalidatedSymbols *Invalidated,
53                      ArrayRef<const MemRegion *> ExplicitRegions,
54                      ArrayRef<const MemRegion *> Regions,
55                      const LocationContext *LCtx, const CallEvent *Call) const;
56   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
57                   const char *Sep) const override;
58   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
59 
60 private:
61   void handleReset(const CallEvent &Call, CheckerContext &C) const;
62   void handleRelease(const CallEvent &Call, CheckerContext &C) const;
63   void handleSwap(const CallEvent &Call, CheckerContext &C) const;
64   void handleGet(const CallEvent &Call, CheckerContext &C) const;
65   bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
66   bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
67                      const MemRegion *ThisRegion) const;
68   bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
69                                 const MemRegion *OtherSmartPtrRegion) const;
70   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
71 
72   using SmartPtrMethodHandlerFn =
73       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
74   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
75       {{"reset"}, &SmartPtrModeling::handleReset},
76       {{"release"}, &SmartPtrModeling::handleRelease},
77       {{"swap", 1}, &SmartPtrModeling::handleSwap},
78       {{"get"}, &SmartPtrModeling::handleGet}};
79 };
80 } // end of anonymous namespace
81 
82 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
83 
84 // Define the inter-checker API.
85 namespace clang {
86 namespace ento {
87 namespace smartptr {
isStdSmartPtrCall(const CallEvent & Call)88 bool isStdSmartPtrCall(const CallEvent &Call) {
89   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
90   if (!MethodDecl || !MethodDecl->getParent())
91     return false;
92 
93   const auto *RecordDecl = MethodDecl->getParent();
94   if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
95     return false;
96 
97   if (RecordDecl->getDeclName().isIdentifier()) {
98     StringRef Name = RecordDecl->getName();
99     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
100   }
101   return false;
102 }
103 
isNullSmartPtr(const ProgramStateRef State,const MemRegion * ThisRegion)104 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
105   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
106   return InnerPointVal &&
107          !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
108 }
109 } // namespace smartptr
110 } // namespace ento
111 } // namespace clang
112 
113 // If a region is removed all of the subregions need to be removed too.
114 static TrackedRegionMapTy
removeTrackedSubregions(TrackedRegionMapTy RegionMap,TrackedRegionMapTy::Factory & RegionMapFactory,const MemRegion * Region)115 removeTrackedSubregions(TrackedRegionMapTy RegionMap,
116                         TrackedRegionMapTy::Factory &RegionMapFactory,
117                         const MemRegion *Region) {
118   if (!Region)
119     return RegionMap;
120   for (const auto &E : RegionMap) {
121     if (E.first->isSubRegionOf(Region))
122       RegionMap = RegionMapFactory.remove(RegionMap, E.first);
123   }
124   return RegionMap;
125 }
126 
updateSwappedRegion(ProgramStateRef State,const MemRegion * Region,const SVal * RegionInnerPointerVal)127 static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
128                                            const MemRegion *Region,
129                                            const SVal *RegionInnerPointerVal) {
130   if (RegionInnerPointerVal) {
131     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
132   } else {
133     State = State->remove<TrackedRegionMap>(Region);
134   }
135   return State;
136 }
137 
138 // Helper method to get the inner pointer type of specialized smart pointer
139 // Returns empty type if not found valid inner pointer type.
getInnerPointerType(const CallEvent & Call,CheckerContext & C)140 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
141   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
142   if (!MethodDecl || !MethodDecl->getParent())
143     return {};
144 
145   const auto *RecordDecl = MethodDecl->getParent();
146   if (!RecordDecl || !RecordDecl->isInStdNamespace())
147     return {};
148 
149   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
150   if (!TSD)
151     return {};
152 
153   auto TemplateArgs = TSD->getTemplateArgs().asArray();
154   if (TemplateArgs.size() == 0)
155     return {};
156   auto InnerValueType = TemplateArgs[0].getAsType();
157   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
158 }
159 
160 // Helper method to pretty print region and avoid extra spacing.
checkAndPrettyPrintRegion(llvm::raw_ostream & OS,const MemRegion * Region)161 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
162                                       const MemRegion *Region) {
163   if (Region->canPrintPretty()) {
164     OS << " ";
165     Region->printPretty(OS);
166   }
167 }
168 
isBoolConversionMethod(const CallEvent & Call) const169 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
170   // TODO: Update CallDescription to support anonymous calls?
171   // TODO: Handle other methods, such as .get() or .release().
172   // But once we do, we'd need a visitor to explain null dereferences
173   // that are found via such modeling.
174   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
175   return CD && CD->getConversionType()->isBooleanType();
176 }
177 
evalCall(const CallEvent & Call,CheckerContext & C) const178 bool SmartPtrModeling::evalCall(const CallEvent &Call,
179                                 CheckerContext &C) const {
180   ProgramStateRef State = C.getState();
181   if (!smartptr::isStdSmartPtrCall(Call))
182     return false;
183 
184   if (isBoolConversionMethod(Call)) {
185     const MemRegion *ThisR =
186         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
187 
188     if (ModelSmartPtrDereference) {
189       // The check for the region is moved is duplicated in handleBoolOperation
190       // method.
191       // FIXME: Once we model std::move for smart pointers clean up this and use
192       // that modeling.
193       handleBoolConversion(Call, C);
194       return true;
195     } else {
196       if (!move::isMovedFrom(State, ThisR)) {
197         // TODO: Model this case as well. At least, avoid invalidation of
198         // globals.
199         return false;
200       }
201 
202       // TODO: Add a note to bug reports describing this decision.
203       C.addTransition(State->BindExpr(
204           Call.getOriginExpr(), C.getLocationContext(),
205           C.getSValBuilder().makeZeroVal(Call.getResultType())));
206 
207       return true;
208     }
209   }
210 
211   if (!ModelSmartPtrDereference)
212     return false;
213 
214   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
215     if (CC->getDecl()->isCopyConstructor())
216       return false;
217 
218     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
219     if (!ThisRegion)
220       return false;
221 
222     if (CC->getDecl()->isMoveConstructor())
223       return handleMoveCtr(Call, C, ThisRegion);
224 
225     if (Call.getNumArgs() == 0) {
226       auto NullVal = C.getSValBuilder().makeNull();
227       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
228 
229       C.addTransition(
230           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
231                                            llvm::raw_ostream &OS) {
232             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
233                 !BR.isInteresting(ThisRegion))
234               return;
235             OS << "Default constructed smart pointer";
236             checkAndPrettyPrintRegion(OS, ThisRegion);
237             OS << " is null";
238           }));
239     } else {
240       const auto *TrackingExpr = Call.getArgExpr(0);
241       assert(TrackingExpr->getType()->isPointerType() &&
242              "Adding a non pointer value to TrackedRegionMap");
243       auto ArgVal = Call.getArgSVal(0);
244       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
245 
246       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
247                                            ArgVal](PathSensitiveBugReport &BR,
248                                                    llvm::raw_ostream &OS) {
249         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
250             !BR.isInteresting(ThisRegion))
251           return;
252         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
253         OS << "Smart pointer";
254         checkAndPrettyPrintRegion(OS, ThisRegion);
255         if (ArgVal.isZeroConstant())
256           OS << " is constructed using a null value";
257         else
258           OS << " is constructed";
259       }));
260     }
261     return true;
262   }
263 
264   if (handleAssignOp(Call, C))
265     return true;
266 
267   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
268   if (!Handler)
269     return false;
270   (this->**Handler)(Call, C);
271 
272   return C.isDifferent();
273 }
274 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const275 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
276                                         CheckerContext &C) const {
277   ProgramStateRef State = C.getState();
278   // Clean up dead regions from the region map.
279   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
280   for (auto E : TrackedRegions) {
281     const MemRegion *Region = E.first;
282     bool IsRegDead = !SymReaper.isLiveRegion(Region);
283 
284     if (IsRegDead)
285       State = State->remove<TrackedRegionMap>(Region);
286   }
287   C.addTransition(State);
288 }
289 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const290 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
291                                   const char *NL, const char *Sep) const {
292   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
293 
294   if (!RS.isEmpty()) {
295     Out << Sep << "Smart ptr regions :" << NL;
296     for (auto I : RS) {
297       I.first->dumpToStream(Out);
298       if (smartptr::isNullSmartPtr(State, I.first))
299         Out << ": Null";
300       else
301         Out << ": Non Null";
302       Out << NL;
303     }
304   }
305 }
306 
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const307 ProgramStateRef SmartPtrModeling::checkRegionChanges(
308     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
309     ArrayRef<const MemRegion *> ExplicitRegions,
310     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
311     const CallEvent *Call) const {
312   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
313   TrackedRegionMapTy::Factory &RegionMapFactory =
314       State->get_context<TrackedRegionMap>();
315   for (const auto *Region : Regions)
316     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
317                                         Region->getBaseRegion());
318   return State->set<TrackedRegionMap>(RegionMap);
319 }
320 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const321 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
322                                         SymbolReaper &SR) const {
323   // Marking tracked symbols alive
324   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
325   for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
326     SVal Val = I->second;
327     for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
328       SR.markLive(*si);
329     }
330   }
331 }
332 
handleReset(const CallEvent & Call,CheckerContext & C) const333 void SmartPtrModeling::handleReset(const CallEvent &Call,
334                                    CheckerContext &C) const {
335   ProgramStateRef State = C.getState();
336   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
337   if (!IC)
338     return;
339 
340   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
341   if (!ThisRegion)
342     return;
343 
344   assert(Call.getArgExpr(0)->getType()->isPointerType() &&
345          "Adding a non pointer value to TrackedRegionMap");
346   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
347   const auto *TrackingExpr = Call.getArgExpr(0);
348   C.addTransition(
349       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
350                                                      llvm::raw_ostream &OS) {
351         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
352             !BR.isInteresting(ThisRegion))
353           return;
354         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
355         OS << "Smart pointer";
356         checkAndPrettyPrintRegion(OS, ThisRegion);
357         OS << " reset using a null value";
358       }));
359   // TODO: Make sure to ivalidate the region in the Store if we don't have
360   // time to model all methods.
361 }
362 
handleRelease(const CallEvent & Call,CheckerContext & C) const363 void SmartPtrModeling::handleRelease(const CallEvent &Call,
364                                      CheckerContext &C) const {
365   ProgramStateRef State = C.getState();
366   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
367   if (!IC)
368     return;
369 
370   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
371   if (!ThisRegion)
372     return;
373 
374   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
375 
376   if (InnerPointVal) {
377     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
378                             *InnerPointVal);
379   }
380 
381   auto ValueToUpdate = C.getSValBuilder().makeNull();
382   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
383 
384   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
385                                                    llvm::raw_ostream &OS) {
386     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
387         !BR.isInteresting(ThisRegion))
388       return;
389 
390     OS << "Smart pointer";
391     checkAndPrettyPrintRegion(OS, ThisRegion);
392     OS << " is released and set to null";
393   }));
394   // TODO: Add support to enable MallocChecker to start tracking the raw
395   // pointer.
396 }
397 
handleSwap(const CallEvent & Call,CheckerContext & C) const398 void SmartPtrModeling::handleSwap(const CallEvent &Call,
399                                   CheckerContext &C) const {
400   // To model unique_ptr::swap() method.
401   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
402   if (!IC)
403     return;
404 
405   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
406   if (!ThisRegion)
407     return;
408 
409   const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
410   if (!ArgRegion)
411     return;
412 
413   auto State = C.getState();
414   const auto *ThisRegionInnerPointerVal =
415       State->get<TrackedRegionMap>(ThisRegion);
416   const auto *ArgRegionInnerPointerVal =
417       State->get<TrackedRegionMap>(ArgRegion);
418 
419   // Swap the tracked region values.
420   State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
421   State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
422 
423   C.addTransition(
424       State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
425                                                   llvm::raw_ostream &OS) {
426         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
427             !BR.isInteresting(ThisRegion))
428           return;
429         BR.markInteresting(ArgRegion);
430         OS << "Swapped null smart pointer";
431         checkAndPrettyPrintRegion(OS, ArgRegion);
432         OS << " with smart pointer";
433         checkAndPrettyPrintRegion(OS, ThisRegion);
434       }));
435 }
436 
handleGet(const CallEvent & Call,CheckerContext & C) const437 void SmartPtrModeling::handleGet(const CallEvent &Call,
438                                  CheckerContext &C) const {
439   ProgramStateRef State = C.getState();
440   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
441   if (!IC)
442     return;
443 
444   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
445   if (!ThisRegion)
446     return;
447 
448   SVal InnerPointerVal;
449   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
450     InnerPointerVal = *InnerValPtr;
451   } else {
452     const auto *CallExpr = Call.getOriginExpr();
453     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
454         CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
455     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
456   }
457 
458   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
459                           InnerPointerVal);
460   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
461   C.addTransition(State);
462 }
463 
handleAssignOp(const CallEvent & Call,CheckerContext & C) const464 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
465                                       CheckerContext &C) const {
466   ProgramStateRef State = C.getState();
467   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
468   if (!OC)
469     return false;
470   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
471   if (OOK != OO_Equal)
472     return false;
473   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
474   if (!ThisRegion)
475     return false;
476 
477   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
478   // In case of 'nullptr' or '0' assigned
479   if (!OtherSmartPtrRegion) {
480     bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
481     if (!AssignedNull)
482       return false;
483     auto NullVal = C.getSValBuilder().makeNull();
484     State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
485     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
486                                                      llvm::raw_ostream &OS) {
487       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
488           !BR.isInteresting(ThisRegion))
489         return;
490       OS << "Smart pointer";
491       checkAndPrettyPrintRegion(OS, ThisRegion);
492       OS << " is assigned to null";
493     }));
494     return true;
495   }
496 
497   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
498 }
499 
handleMoveCtr(const CallEvent & Call,CheckerContext & C,const MemRegion * ThisRegion) const500 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
501                                      const MemRegion *ThisRegion) const {
502   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
503   if (!OtherSmartPtrRegion)
504     return false;
505 
506   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
507 }
508 
updateMovedSmartPointers(CheckerContext & C,const MemRegion * ThisRegion,const MemRegion * OtherSmartPtrRegion) const509 bool SmartPtrModeling::updateMovedSmartPointers(
510     CheckerContext &C, const MemRegion *ThisRegion,
511     const MemRegion *OtherSmartPtrRegion) const {
512   ProgramStateRef State = C.getState();
513   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
514   if (OtherInnerPtr) {
515     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
516     auto NullVal = C.getSValBuilder().makeNull();
517     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
518     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
519 
520     C.addTransition(
521         State,
522         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
523                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
524           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
525             return;
526           if (BR.isInteresting(OtherSmartPtrRegion)) {
527             OS << "Smart pointer";
528             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
529             OS << " is null after being moved to";
530             checkAndPrettyPrintRegion(OS, ThisRegion);
531           }
532           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
533             OS << "A null pointer value is moved to";
534             checkAndPrettyPrintRegion(OS, ThisRegion);
535             BR.markInteresting(OtherSmartPtrRegion);
536           }
537         }));
538     return true;
539   } else {
540     // In case we dont know anything about value we are moving from
541     // remove the entry from map for which smart pointer got moved to.
542     auto NullVal = C.getSValBuilder().makeNull();
543     State = State->remove<TrackedRegionMap>(ThisRegion);
544     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
545     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
546                                          ThisRegion](PathSensitiveBugReport &BR,
547                                                      llvm::raw_ostream &OS) {
548       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
549           !BR.isInteresting(OtherSmartPtrRegion))
550         return;
551       OS << "Smart pointer";
552       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
553       OS << " is null after; previous value moved to";
554       checkAndPrettyPrintRegion(OS, ThisRegion);
555     }));
556     return true;
557   }
558   return false;
559 }
560 
handleBoolConversion(const CallEvent & Call,CheckerContext & C) const561 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
562                                             CheckerContext &C) const {
563   // To model unique_ptr::operator bool
564   ProgramStateRef State = C.getState();
565   const Expr *CallExpr = Call.getOriginExpr();
566   const MemRegion *ThisRegion =
567       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
568 
569   SVal InnerPointerVal;
570   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
571     InnerPointerVal = *InnerValPtr;
572   } else {
573     // In case of inner pointer SVal is not available we create
574     // conjureSymbolVal for inner pointer value.
575     auto InnerPointerType = getInnerPointerType(Call, C);
576     if (InnerPointerType.isNull())
577       return;
578 
579     const LocationContext *LC = C.getLocationContext();
580     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
581         CallExpr, LC, InnerPointerType, C.blockCount());
582     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
583   }
584 
585   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
586     State = State->BindExpr(CallExpr, C.getLocationContext(),
587                             C.getSValBuilder().makeTruthVal(false));
588 
589     C.addTransition(State);
590     return;
591   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
592     State = State->BindExpr(CallExpr, C.getLocationContext(),
593                             C.getSValBuilder().makeTruthVal(true));
594 
595     C.addTransition(State);
596     return;
597   } else if (move::isMovedFrom(State, ThisRegion)) {
598     C.addTransition(
599         State->BindExpr(CallExpr, C.getLocationContext(),
600                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
601     return;
602   } else {
603     ProgramStateRef NotNullState, NullState;
604     std::tie(NotNullState, NullState) =
605         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
606 
607     auto NullVal = C.getSValBuilder().makeNull();
608     // Explicitly tracking the region as null.
609     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
610 
611     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
612                                     C.getSValBuilder().makeTruthVal(false));
613     C.addTransition(NullState, C.getNoteTag(
614                                    [ThisRegion](PathSensitiveBugReport &BR,
615                                                 llvm::raw_ostream &OS) {
616                                      OS << "Assuming smart pointer";
617                                      checkAndPrettyPrintRegion(OS, ThisRegion);
618                                      OS << " is null";
619                                    },
620                                    /*IsPrunable=*/true));
621     NotNullState =
622         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
623                                C.getSValBuilder().makeTruthVal(true));
624     C.addTransition(
625         NotNullState,
626         C.getNoteTag(
627             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
628               OS << "Assuming smart pointer";
629               checkAndPrettyPrintRegion(OS, ThisRegion);
630               OS << " is non-null";
631             },
632             /*IsPrunable=*/true));
633     return;
634   }
635 }
636 
registerSmartPtrModeling(CheckerManager & Mgr)637 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
638   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
639   Checker->ModelSmartPtrDereference =
640       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
641           Checker, "ModelSmartPtrDereference");
642 }
643 
shouldRegisterSmartPtrModeling(const CheckerManager & mgr)644 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
645   const LangOptions &LO = mgr.getLangOpts();
646   return LO.CPlusPlus;
647 }
648