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 
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap,const MemRegion *,SVal)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 
isStdSmartPtr(const CXXRecordDecl * RD)117 static bool isStdSmartPtr(const CXXRecordDecl *RD) {
118   return hasStdClassWithName(RD, STD_PTR_NAMES);
119 }
120 
isStdSmartPtr(const Expr * E)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 {
isStdSmartPtrCall(const CallEvent & Call)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 
isStdSmartPtr(const CXXRecordDecl * RD)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 
isStdSmartPtr(const Expr * E)147 bool isStdSmartPtr(const Expr *E) {
148   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
149 }
150 
isNullSmartPtr(const ProgramStateRef State,const MemRegion * ThisRegion)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
removeTrackedSubregions(TrackedRegionMapTy RegionMap,TrackedRegionMapTy::Factory & RegionMapFactory,const MemRegion * Region)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 
updateSwappedRegion(ProgramStateRef State,const MemRegion * Region,const SVal * RegionInnerPointerVal)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 
getInnerPointerType(CheckerContext C,const CXXRecordDecl * RD)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,
getPointerTypeFromTemplateArg(const CallEvent & Call,CheckerContext & C)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.
getInnerPointerType(const CallEvent & Call,CheckerContext & C)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.
checkAndPrettyPrintRegion(llvm::raw_ostream & OS,const MemRegion * Region)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 
isBoolConversionMethod(const CallEvent & Call) const235 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 
isStdBasicOstream(const Expr * E)246 bool isStdBasicOstream(const Expr *E) {
247   const auto *RD = E->getType()->getAsCXXRecordDecl();
248   return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
249 }
250 
isStdFunctionCall(const CallEvent & Call)251 static bool isStdFunctionCall(const CallEvent &Call) {
252   return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
253 }
254 
isStdOstreamOperatorCall(const CallEvent & Call)255 bool isStdOstreamOperatorCall(const CallEvent &Call) {
256   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
257     return false;
258   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
259   if (!FC)
260     return false;
261   const FunctionDecl *FD = FC->getDecl();
262   if (!FD->isOverloadedOperator())
263     return false;
264   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
265   if (OOK != clang::OO_LessLess)
266     return false;
267   return isStdSmartPtr(Call.getArgExpr(1)) &&
268          isStdBasicOstream(Call.getArgExpr(0));
269 }
270 
isPotentiallyComparisionOpCall(const CallEvent & Call)271 static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
272   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
273     return false;
274   return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
275          smartptr::isStdSmartPtr(Call.getArgExpr(1));
276 }
277 
evalCall(const CallEvent & Call,CheckerContext & C) const278 bool SmartPtrModeling::evalCall(const CallEvent &Call,
279                                 CheckerContext &C) const {
280 
281   ProgramStateRef State = C.getState();
282 
283   // If any one of the arg is a unique_ptr, then
284   // we can try this function
285   if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
286     if (handleComparisionOp(Call, C))
287       return true;
288 
289   if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
290     return handleOstreamOperator(Call, C);
291 
292   if (Call.isCalled(StdSwapCall)) {
293     // Check the first arg, if it is of std::unique_ptr type.
294     assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
295     const Expr *FirstArg = Call.getArgExpr(0);
296     if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl()))
297       return false;
298     return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
299   }
300 
301   if (Call.isCalled(StdMakeUniqueCall) ||
302       Call.isCalled(StdMakeUniqueForOverwriteCall)) {
303     if (!ModelSmartPtrDereference)
304       return false;
305 
306     const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction();
307     if (!ThisRegionOpt)
308       return false;
309 
310     const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
311         Call.getOriginExpr(), C.getLocationContext(),
312         getPointerTypeFromTemplateArg(Call, C), C.blockCount());
313 
314     const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
315     State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
316     State = State->assume(PtrVal, true);
317 
318     // TODO: ExprEngine should do this for us.
319     // For a bit more context:
320     // 1) Why do we need this? Since we are modelling a "function"
321     // that returns a constructed object we need to store this information in
322     // the program state.
323     //
324     // 2) Why does this work?
325     // `updateObjectsUnderConstruction` does exactly as it sounds.
326     //
327     // 3) How should it look like when moved to the Engine?
328     // It would be nice if we can just
329     // pretend we don't need to know about this - ie, completely automatic work.
330     // However, realistically speaking, I think we would need to "signal" the
331     // ExprEngine evalCall handler that we are constructing an object with this
332     // function call (constructors obviously construct, hence can be
333     // automatically deduced).
334     auto &Engine = State->getStateManager().getOwningEngine();
335     State = Engine.updateObjectsUnderConstruction(
336         *ThisRegionOpt, nullptr, State, C.getLocationContext(),
337         Call.getConstructionContext(), {});
338 
339     // We don't leave a note here since it is guaranteed the
340     // unique_ptr from this call is non-null (hence is safe to de-reference).
341     C.addTransition(State);
342     return true;
343   }
344 
345   if (!smartptr::isStdSmartPtrCall(Call))
346     return false;
347 
348   if (isBoolConversionMethod(Call)) {
349     const MemRegion *ThisR =
350         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
351 
352     if (ModelSmartPtrDereference) {
353       // The check for the region is moved is duplicated in handleBoolOperation
354       // method.
355       // FIXME: Once we model std::move for smart pointers clean up this and use
356       // that modeling.
357       handleBoolConversion(Call, C);
358       return true;
359     } else {
360       if (!move::isMovedFrom(State, ThisR)) {
361         // TODO: Model this case as well. At least, avoid invalidation of
362         // globals.
363         return false;
364       }
365 
366       // TODO: Add a note to bug reports describing this decision.
367       C.addTransition(State->BindExpr(
368           Call.getOriginExpr(), C.getLocationContext(),
369           C.getSValBuilder().makeZeroVal(Call.getResultType())));
370 
371       return true;
372     }
373   }
374 
375   if (!ModelSmartPtrDereference)
376     return false;
377 
378   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
379     if (CC->getDecl()->isCopyConstructor())
380       return false;
381 
382     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
383     if (!ThisRegion)
384       return false;
385 
386     if (CC->getDecl()->isMoveConstructor())
387       return handleMoveCtr(Call, C, ThisRegion);
388 
389     if (Call.getNumArgs() == 0) {
390       auto NullVal = C.getSValBuilder().makeNull();
391       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
392 
393       C.addTransition(
394           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
395                                            llvm::raw_ostream &OS) {
396             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
397                 !BR.isInteresting(ThisRegion))
398               return;
399             OS << "Default constructed smart pointer";
400             checkAndPrettyPrintRegion(OS, ThisRegion);
401             OS << " is null";
402           }));
403     } else {
404       const auto *TrackingExpr = Call.getArgExpr(0);
405       assert(TrackingExpr->getType()->isPointerType() &&
406              "Adding a non pointer value to TrackedRegionMap");
407       auto ArgVal = Call.getArgSVal(0);
408       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
409 
410       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
411                                            ArgVal](PathSensitiveBugReport &BR,
412                                                    llvm::raw_ostream &OS) {
413         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
414             !BR.isInteresting(ThisRegion))
415           return;
416         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
417         OS << "Smart pointer";
418         checkAndPrettyPrintRegion(OS, ThisRegion);
419         if (ArgVal.isZeroConstant())
420           OS << " is constructed using a null value";
421         else
422           OS << " is constructed";
423       }));
424     }
425     return true;
426   }
427 
428   if (handleAssignOp(Call, C))
429     return true;
430 
431   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
432   if (!Handler)
433     return false;
434   (this->**Handler)(Call, C);
435 
436   return C.isDifferent();
437 }
438 
retrieveOrConjureInnerPtrVal(ProgramStateRef State,const MemRegion * ThisRegion,const Expr * E,QualType Type,CheckerContext & C) const439 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440     ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
441     QualType Type, CheckerContext &C) const {
442   const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
443   if (Ptr)
444     return {*Ptr, State};
445   auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
446                                                  Type, C.blockCount());
447   State = State->set<TrackedRegionMap>(ThisRegion, Val);
448   return {Val, State};
449 }
450 
handleComparisionOp(const CallEvent & Call,CheckerContext & C) const451 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
452                                            CheckerContext &C) const {
453   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
454   if (!FC)
455     return false;
456   const FunctionDecl *FD = FC->getDecl();
457   if (!FD->isOverloadedOperator())
458     return false;
459   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
460   if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
461         OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
462         OOK == OO_Spaceship))
463     return false;
464 
465   // There are some special cases about which we can infer about
466   // the resulting answer.
467   // For reference, there is a discussion at https://reviews.llvm.org/D104616.
468   // Also, the cppreference page is good to look at
469   // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
470 
471   auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
472                                 SVal S) -> std::pair<SVal, ProgramStateRef> {
473     if (S.isZeroConstant()) {
474       return {S, State};
475     }
476     const MemRegion *Reg = S.getAsRegion();
477     assert(Reg &&
478            "this pointer of std::unique_ptr should be obtainable as MemRegion");
479     QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
480     return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
481   };
482 
483   SVal First = Call.getArgSVal(0);
484   SVal Second = Call.getArgSVal(1);
485   const auto *FirstExpr = Call.getArgExpr(0);
486   const auto *SecondExpr = Call.getArgExpr(1);
487 
488   const auto *ResultExpr = Call.getOriginExpr();
489   const auto *LCtx = C.getLocationContext();
490   auto &Bldr = C.getSValBuilder();
491   ProgramStateRef State = C.getState();
492 
493   SVal FirstPtrVal, SecondPtrVal;
494   std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
495   std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
496   BinaryOperatorKind BOK =
497       operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
498   auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499                                Call.getResultType());
500 
501   if (OOK != OO_Spaceship) {
502     ProgramStateRef TrueState, FalseState;
503     std::tie(TrueState, FalseState) =
504         State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505     if (TrueState)
506       C.addTransition(
507           TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
508     if (FalseState)
509       C.addTransition(
510           FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
511   } else {
512     C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
513   }
514   return true;
515 }
516 
handleOstreamOperator(const CallEvent & Call,CheckerContext & C) const517 bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
518                                              CheckerContext &C) const {
519   // operator<< does not modify the smart pointer.
520   // And we don't really have much of modelling of basic_ostream.
521   // So, we are better off:
522   // 1) Invalidating the mem-region of the ostream object at hand.
523   // 2) Setting the SVal of the basic_ostream as the return value.
524   // Not very satisfying, but it gets the job done, and is better
525   // than the default handling. :)
526 
527   ProgramStateRef State = C.getState();
528   const auto StreamVal = Call.getArgSVal(0);
529   const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530   if (!StreamThisRegion)
531     return false;
532   State =
533       State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
534                                C.blockCount(), C.getLocationContext(), false);
535   State =
536       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
537   C.addTransition(State);
538   return true;
539 }
540 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const541 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
542                                         CheckerContext &C) const {
543   ProgramStateRef State = C.getState();
544   // Clean up dead regions from the region map.
545   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
546   for (auto E : TrackedRegions) {
547     const MemRegion *Region = E.first;
548     bool IsRegDead = !SymReaper.isLiveRegion(Region);
549 
550     if (IsRegDead)
551       State = State->remove<TrackedRegionMap>(Region);
552   }
553   C.addTransition(State);
554 }
555 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const556 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
557                                   const char *NL, const char *Sep) const {
558   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
559 
560   if (!RS.isEmpty()) {
561     Out << Sep << "Smart ptr regions :" << NL;
562     for (auto I : RS) {
563       I.first->dumpToStream(Out);
564       if (smartptr::isNullSmartPtr(State, I.first))
565         Out << ": Null";
566       else
567         Out << ": Non Null";
568       Out << NL;
569     }
570   }
571 }
572 
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const573 ProgramStateRef SmartPtrModeling::checkRegionChanges(
574     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
575     ArrayRef<const MemRegion *> ExplicitRegions,
576     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
577     const CallEvent *Call) const {
578   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
579   TrackedRegionMapTy::Factory &RegionMapFactory =
580       State->get_context<TrackedRegionMap>();
581   for (const auto *Region : Regions)
582     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
583                                         Region->getBaseRegion());
584   return State->set<TrackedRegionMap>(RegionMap);
585 }
586 
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const587 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
588                                         SymbolReaper &SR) const {
589   // Marking tracked symbols alive
590   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
591   for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
592     SVal Val = I->second;
593     for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
594       SR.markLive(*si);
595     }
596   }
597 }
598 
handleReset(const CallEvent & Call,CheckerContext & C) const599 void SmartPtrModeling::handleReset(const CallEvent &Call,
600                                    CheckerContext &C) const {
601   ProgramStateRef State = C.getState();
602   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
603   if (!IC)
604     return;
605 
606   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
607   if (!ThisRegion)
608     return;
609 
610   assert(Call.getArgExpr(0)->getType()->isPointerType() &&
611          "Adding a non pointer value to TrackedRegionMap");
612   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
613   const auto *TrackingExpr = Call.getArgExpr(0);
614   C.addTransition(
615       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
616                                                      llvm::raw_ostream &OS) {
617         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
618             !BR.isInteresting(ThisRegion))
619           return;
620         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
621         OS << "Smart pointer";
622         checkAndPrettyPrintRegion(OS, ThisRegion);
623         OS << " reset using a null value";
624       }));
625   // TODO: Make sure to ivalidate the region in the Store if we don't have
626   // time to model all methods.
627 }
628 
handleRelease(const CallEvent & Call,CheckerContext & C) const629 void SmartPtrModeling::handleRelease(const CallEvent &Call,
630                                      CheckerContext &C) const {
631   ProgramStateRef State = C.getState();
632   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
633   if (!IC)
634     return;
635 
636   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
637   if (!ThisRegion)
638     return;
639 
640   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
641 
642   if (InnerPointVal) {
643     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
644                             *InnerPointVal);
645   }
646 
647   auto ValueToUpdate = C.getSValBuilder().makeNull();
648   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
649 
650   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
651                                                    llvm::raw_ostream &OS) {
652     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
653         !BR.isInteresting(ThisRegion))
654       return;
655 
656     OS << "Smart pointer";
657     checkAndPrettyPrintRegion(OS, ThisRegion);
658     OS << " is released and set to null";
659   }));
660   // TODO: Add support to enable MallocChecker to start tracking the raw
661   // pointer.
662 }
663 
handleSwapMethod(const CallEvent & Call,CheckerContext & C) const664 void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
665                                         CheckerContext &C) const {
666   // To model unique_ptr::swap() method.
667   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
668   if (!IC)
669     return;
670 
671   auto State = C.getState();
672   handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
673 }
674 
handleSwap(ProgramStateRef State,SVal First,SVal Second,CheckerContext & C) const675 bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
676                                   SVal Second, CheckerContext &C) const {
677   const MemRegion *FirstThisRegion = First.getAsRegion();
678   if (!FirstThisRegion)
679     return false;
680   const MemRegion *SecondThisRegion = Second.getAsRegion();
681   if (!SecondThisRegion)
682     return false;
683 
684   const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
685   const auto *SecondInnerPtrVal =
686       State->get<TrackedRegionMap>(SecondThisRegion);
687 
688   State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
689   State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
690 
691   C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
692                                           PathSensitiveBugReport &BR,
693                                           llvm::raw_ostream &OS) {
694     if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
695       return;
696     if (BR.isInteresting(FirstThisRegion) &&
697         !BR.isInteresting(SecondThisRegion)) {
698       BR.markInteresting(SecondThisRegion);
699       BR.markNotInteresting(FirstThisRegion);
700     }
701     if (BR.isInteresting(SecondThisRegion) &&
702         !BR.isInteresting(FirstThisRegion)) {
703       BR.markInteresting(FirstThisRegion);
704       BR.markNotInteresting(SecondThisRegion);
705     }
706     // TODO: We need to emit some note here probably!!
707   }));
708 
709   return true;
710 }
711 
handleGet(const CallEvent & Call,CheckerContext & C) const712 void SmartPtrModeling::handleGet(const CallEvent &Call,
713                                  CheckerContext &C) const {
714   ProgramStateRef State = C.getState();
715   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
716   if (!IC)
717     return;
718 
719   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
720   if (!ThisRegion)
721     return;
722 
723   SVal InnerPointerVal;
724   std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
725       State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
726   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
727                           InnerPointerVal);
728   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
729   C.addTransition(State);
730 }
731 
handleAssignOp(const CallEvent & Call,CheckerContext & C) const732 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
733                                       CheckerContext &C) const {
734   ProgramStateRef State = C.getState();
735   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
736   if (!OC)
737     return false;
738   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
739   if (OOK != OO_Equal)
740     return false;
741   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
742   if (!ThisRegion)
743     return false;
744 
745   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
746   // In case of 'nullptr' or '0' assigned
747   if (!OtherSmartPtrRegion) {
748     bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
749     if (!AssignedNull)
750       return false;
751     auto NullVal = C.getSValBuilder().makeNull();
752     State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
753     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
754                                                      llvm::raw_ostream &OS) {
755       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
756           !BR.isInteresting(ThisRegion))
757         return;
758       OS << "Smart pointer";
759       checkAndPrettyPrintRegion(OS, ThisRegion);
760       OS << " is assigned to null";
761     }));
762     return true;
763   }
764 
765   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
766 }
767 
handleMoveCtr(const CallEvent & Call,CheckerContext & C,const MemRegion * ThisRegion) const768 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
769                                      const MemRegion *ThisRegion) const {
770   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
771   if (!OtherSmartPtrRegion)
772     return false;
773 
774   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
775 }
776 
updateMovedSmartPointers(CheckerContext & C,const MemRegion * ThisRegion,const MemRegion * OtherSmartPtrRegion) const777 bool SmartPtrModeling::updateMovedSmartPointers(
778     CheckerContext &C, const MemRegion *ThisRegion,
779     const MemRegion *OtherSmartPtrRegion) const {
780   ProgramStateRef State = C.getState();
781   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
782   if (OtherInnerPtr) {
783     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
784     auto NullVal = C.getSValBuilder().makeNull();
785     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
786     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
787 
788     C.addTransition(
789         State,
790         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
791                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
792           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
793             return;
794           if (BR.isInteresting(OtherSmartPtrRegion)) {
795             OS << "Smart pointer";
796             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
797             OS << " is null after being moved to";
798             checkAndPrettyPrintRegion(OS, ThisRegion);
799           }
800           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
801             OS << "A null pointer value is moved to";
802             checkAndPrettyPrintRegion(OS, ThisRegion);
803             BR.markInteresting(OtherSmartPtrRegion);
804           }
805         }));
806     return true;
807   } else {
808     // In case we dont know anything about value we are moving from
809     // remove the entry from map for which smart pointer got moved to.
810     auto NullVal = C.getSValBuilder().makeNull();
811     State = State->remove<TrackedRegionMap>(ThisRegion);
812     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
813     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
814                                          ThisRegion](PathSensitiveBugReport &BR,
815                                                      llvm::raw_ostream &OS) {
816       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
817           !BR.isInteresting(OtherSmartPtrRegion))
818         return;
819       OS << "Smart pointer";
820       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
821       OS << " is null after; previous value moved to";
822       checkAndPrettyPrintRegion(OS, ThisRegion);
823     }));
824     return true;
825   }
826   return false;
827 }
828 
handleBoolConversion(const CallEvent & Call,CheckerContext & C) const829 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
830                                             CheckerContext &C) const {
831   // To model unique_ptr::operator bool
832   ProgramStateRef State = C.getState();
833   const Expr *CallExpr = Call.getOriginExpr();
834   const MemRegion *ThisRegion =
835       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
836 
837   SVal InnerPointerVal;
838   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
839     InnerPointerVal = *InnerValPtr;
840   } else {
841     // In case of inner pointer SVal is not available we create
842     // conjureSymbolVal for inner pointer value.
843     auto InnerPointerType = getInnerPointerType(Call, C);
844     if (InnerPointerType.isNull())
845       return;
846 
847     const LocationContext *LC = C.getLocationContext();
848     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
849         CallExpr, LC, InnerPointerType, C.blockCount());
850     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
851   }
852 
853   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
854     State = State->BindExpr(CallExpr, C.getLocationContext(),
855                             C.getSValBuilder().makeTruthVal(false));
856 
857     C.addTransition(State);
858     return;
859   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
860     State = State->BindExpr(CallExpr, C.getLocationContext(),
861                             C.getSValBuilder().makeTruthVal(true));
862 
863     C.addTransition(State);
864     return;
865   } else if (move::isMovedFrom(State, ThisRegion)) {
866     C.addTransition(
867         State->BindExpr(CallExpr, C.getLocationContext(),
868                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
869     return;
870   } else {
871     ProgramStateRef NotNullState, NullState;
872     std::tie(NotNullState, NullState) =
873         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
874 
875     auto NullVal = C.getSValBuilder().makeNull();
876     // Explicitly tracking the region as null.
877     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
878 
879     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
880                                     C.getSValBuilder().makeTruthVal(false));
881     C.addTransition(NullState, C.getNoteTag(
882                                    [ThisRegion](PathSensitiveBugReport &BR,
883                                                 llvm::raw_ostream &OS) {
884                                      OS << "Assuming smart pointer";
885                                      checkAndPrettyPrintRegion(OS, ThisRegion);
886                                      OS << " is null";
887                                    },
888                                    /*IsPrunable=*/true));
889     NotNullState =
890         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
891                                C.getSValBuilder().makeTruthVal(true));
892     C.addTransition(
893         NotNullState,
894         C.getNoteTag(
895             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
896               OS << "Assuming smart pointer";
897               checkAndPrettyPrintRegion(OS, ThisRegion);
898               OS << " is non-null";
899             },
900             /*IsPrunable=*/true));
901     return;
902   }
903 }
904 
registerSmartPtrModeling(CheckerManager & Mgr)905 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
906   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
907   Checker->ModelSmartPtrDereference =
908       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
909           Checker, "ModelSmartPtrDereference");
910 }
911 
shouldRegisterSmartPtrModeling(const CheckerManager & mgr)912 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
913   const LangOptions &LO = mgr.getLangOpts();
914   return LO.CPlusPlus;
915 }
916