| // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This file defines a checker that models various aspects of | 
 | // C++ smart pointer behavior. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "Move.h" | 
 | #include "SmartPtr.h" | 
 |  | 
 | #include "clang/AST/DeclCXX.h" | 
 | #include "clang/AST/DeclarationName.h" | 
 | #include "clang/AST/ExprCXX.h" | 
 | #include "clang/AST/Type.h" | 
 | #include "clang/Basic/LLVM.h" | 
 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | 
 | #include "clang/StaticAnalyzer/Core/Checker.h" | 
 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" | 
 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | 
 | #include "llvm/ADT/STLExtras.h" | 
 | #include "llvm/Support/ErrorHandling.h" | 
 | #include <optional> | 
 |  | 
 | using namespace clang; | 
 | using namespace ento; | 
 |  | 
 | namespace { | 
 |  | 
 | class SmartPtrModeling | 
 |     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, | 
 |                      check::LiveSymbols> { | 
 |  | 
 |   bool isBoolConversionMethod(const CallEvent &Call) const; | 
 |  | 
 | public: | 
 |   // Whether the checker should model for null dereferences of smart pointers. | 
 |   bool ModelSmartPtrDereference = false; | 
 |   bool evalCall(const CallEvent &Call, CheckerContext &C) const; | 
 |   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; | 
 |   ProgramStateRef | 
 |   checkRegionChanges(ProgramStateRef State, | 
 |                      const InvalidatedSymbols *Invalidated, | 
 |                      ArrayRef<const MemRegion *> ExplicitRegions, | 
 |                      ArrayRef<const MemRegion *> Regions, | 
 |                      const LocationContext *LCtx, const CallEvent *Call) const; | 
 |   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, | 
 |                   const char *Sep) const override; | 
 |   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; | 
 |  | 
 | private: | 
 |   void handleReset(const CallEvent &Call, CheckerContext &C) const; | 
 |   void handleRelease(const CallEvent &Call, CheckerContext &C) const; | 
 |   void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; | 
 |   void handleGet(const CallEvent &Call, CheckerContext &C) const; | 
 |   bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; | 
 |   bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, | 
 |                      const MemRegion *ThisRegion) const; | 
 |   bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, | 
 |                                 const MemRegion *OtherSmartPtrRegion, | 
 |                                 const CallEvent &Call) const; | 
 |   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; | 
 |   bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; | 
 |   bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; | 
 |   bool handleSwap(ProgramStateRef State, SVal First, SVal Second, | 
 |                   CheckerContext &C) const; | 
 |   std::pair<SVal, ProgramStateRef> retrieveOrConjureInnerPtrVal( | 
 |       ProgramStateRef State, const MemRegion *ThisRegion, | 
 |       ConstCFGElementRef Elem, QualType Type, CheckerContext &C) const; | 
 |  | 
 |   using SmartPtrMethodHandlerFn = | 
 |       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; | 
 |   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ | 
 |       {{CDM::CXXMethod, {"reset"}}, &SmartPtrModeling::handleReset}, | 
 |       {{CDM::CXXMethod, {"release"}}, &SmartPtrModeling::handleRelease}, | 
 |       {{CDM::CXXMethod, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, | 
 |       {{CDM::CXXMethod, {"get"}}, &SmartPtrModeling::handleGet}}; | 
 |   const CallDescription StdSwapCall{CDM::SimpleFunc, {"std", "swap"}, 2}; | 
 |   const CallDescriptionSet MakeUniqueVariants{ | 
 |       {CDM::SimpleFunc, {"std", "make_unique"}}, | 
 |       {CDM::SimpleFunc, {"std", "make_unique_for_overwrite"}}}; | 
 | }; | 
 | } // end of anonymous namespace | 
 |  | 
 | REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) | 
 |  | 
 | // Checks if RD has name in Names and is in std namespace | 
 | static bool hasStdClassWithName(const CXXRecordDecl *RD, | 
 |                                 ArrayRef<llvm::StringLiteral> Names) { | 
 |   if (!RD || !RD->getDeclContext()->isStdNamespace()) | 
 |     return false; | 
 |   if (RD->getDeclName().isIdentifier()) | 
 |     return llvm::is_contained(Names, RD->getName()); | 
 |   return false; | 
 | } | 
 |  | 
 | constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr", | 
 |                                                  "weak_ptr"}; | 
 |  | 
 | static bool isStdSmartPtr(const CXXRecordDecl *RD) { | 
 |   return hasStdClassWithName(RD, STD_PTR_NAMES); | 
 | } | 
 |  | 
 | static bool isStdSmartPtr(const Expr *E) { | 
 |   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); | 
 | } | 
 |  | 
 | // Define the inter-checker API. | 
 | namespace clang { | 
 | namespace ento { | 
 | namespace smartptr { | 
 | bool isStdSmartPtrCall(const CallEvent &Call) { | 
 |   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); | 
 |   if (!MethodDecl || !MethodDecl->getParent()) | 
 |     return false; | 
 |   return isStdSmartPtr(MethodDecl->getParent()); | 
 | } | 
 |  | 
 | bool isStdSmartPtr(const CXXRecordDecl *RD) { | 
 |   if (!RD || !RD->getDeclContext()->isStdNamespace()) | 
 |     return false; | 
 |  | 
 |   if (RD->getDeclName().isIdentifier()) { | 
 |     StringRef Name = RD->getName(); | 
 |     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool isStdSmartPtr(const Expr *E) { | 
 |   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); | 
 | } | 
 |  | 
 | bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { | 
 |   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); | 
 |   return InnerPointVal && | 
 |          !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); | 
 | } | 
 | } // namespace smartptr | 
 | } // namespace ento | 
 | } // namespace clang | 
 |  | 
 | // If a region is removed all of the subregions need to be removed too. | 
 | static TrackedRegionMapTy | 
 | removeTrackedSubregions(TrackedRegionMapTy RegionMap, | 
 |                         TrackedRegionMapTy::Factory &RegionMapFactory, | 
 |                         const MemRegion *Region) { | 
 |   if (!Region) | 
 |     return RegionMap; | 
 |   for (const auto &E : RegionMap) { | 
 |     if (E.first->isSubRegionOf(Region)) | 
 |       RegionMap = RegionMapFactory.remove(RegionMap, E.first); | 
 |   } | 
 |   return RegionMap; | 
 | } | 
 |  | 
 | static ProgramStateRef updateSwappedRegion(ProgramStateRef State, | 
 |                                            const MemRegion *Region, | 
 |                                            const SVal *RegionInnerPointerVal) { | 
 |   if (RegionInnerPointerVal) { | 
 |     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); | 
 |   } else { | 
 |     State = State->remove<TrackedRegionMap>(Region); | 
 |   } | 
 |   return State; | 
 | } | 
 |  | 
 | static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { | 
 |   if (!RD || !RD->isInStdNamespace()) | 
 |     return {}; | 
 |  | 
 |   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); | 
 |   if (!TSD) | 
 |     return {}; | 
 |  | 
 |   auto TemplateArgs = TSD->getTemplateArgs().asArray(); | 
 |   if (TemplateArgs.empty()) | 
 |     return {}; | 
 |   auto InnerValueType = TemplateArgs[0].getAsType(); | 
 |   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); | 
 | } | 
 |  | 
 | // This is for use with standalone-functions like std::make_unique, | 
 | // std::make_unique_for_overwrite, etc. It reads the template parameter and | 
 | // returns the pointer type corresponding to it, | 
 | static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, | 
 |                                               CheckerContext &C) { | 
 |   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); | 
 |   if (!FD || !FD->getPrimaryTemplate()) | 
 |     return {}; | 
 |   const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); | 
 |   if (TemplateArgs.size() == 0) | 
 |     return {}; | 
 |   auto ValueType = TemplateArgs[0].getAsType(); | 
 |   return C.getASTContext().getPointerType(ValueType.getCanonicalType()); | 
 | } | 
 |  | 
 | // Helper method to get the inner pointer type of specialized smart pointer | 
 | // Returns empty type if not found valid inner pointer type. | 
 | static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { | 
 |   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); | 
 |   if (!MethodDecl || !MethodDecl->getParent()) | 
 |     return {}; | 
 |  | 
 |   const auto *RecordDecl = MethodDecl->getParent(); | 
 |   return getInnerPointerType(C, RecordDecl); | 
 | } | 
 |  | 
 | // Helper method to pretty print region and avoid extra spacing. | 
 | static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, | 
 |                                       const MemRegion *Region) { | 
 |   if (Region->canPrintPretty()) { | 
 |     OS << " "; | 
 |     Region->printPretty(OS); | 
 |   } | 
 | } | 
 |  | 
 | bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { | 
 |   // TODO: Update CallDescription to support anonymous calls? | 
 |   // TODO: Handle other methods, such as .get() or .release(). | 
 |   // But once we do, we'd need a visitor to explain null dereferences | 
 |   // that are found via such modeling. | 
 |   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); | 
 |   return CD && CD->getConversionType()->isBooleanType(); | 
 | } | 
 |  | 
 | constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"}; | 
 |  | 
 | static bool isStdBasicOstream(const Expr *E) { | 
 |   const auto *RD = E->getType()->getAsCXXRecordDecl(); | 
 |   return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES); | 
 | } | 
 |  | 
 | static bool isStdFunctionCall(const CallEvent &Call) { | 
 |   return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace(); | 
 | } | 
 |  | 
 | static bool isStdOstreamOperatorCall(const CallEvent &Call) { | 
 |   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) | 
 |     return false; | 
 |   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); | 
 |   if (!FC) | 
 |     return false; | 
 |   const FunctionDecl *FD = FC->getDecl(); | 
 |   if (!FD->isOverloadedOperator()) | 
 |     return false; | 
 |   const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); | 
 |   if (OOK != clang::OO_LessLess) | 
 |     return false; | 
 |   return isStdSmartPtr(Call.getArgExpr(1)) && | 
 |          isStdBasicOstream(Call.getArgExpr(0)); | 
 | } | 
 |  | 
 | static bool isPotentiallyComparisionOpCall(const CallEvent &Call) { | 
 |   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) | 
 |     return false; | 
 |   return smartptr::isStdSmartPtr(Call.getArgExpr(0)) || | 
 |          smartptr::isStdSmartPtr(Call.getArgExpr(1)); | 
 | } | 
 |  | 
 | bool SmartPtrModeling::evalCall(const CallEvent &Call, | 
 |                                 CheckerContext &C) const { | 
 |  | 
 |   ProgramStateRef State = C.getState(); | 
 |  | 
 |   // If any one of the arg is a unique_ptr, then | 
 |   // we can try this function | 
 |   if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call)) | 
 |     if (handleComparisionOp(Call, C)) | 
 |       return true; | 
 |  | 
 |   if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) | 
 |     return handleOstreamOperator(Call, C); | 
 |  | 
 |   if (StdSwapCall.matches(Call)) { | 
 |     // Check the first arg, if it is of std::unique_ptr type. | 
 |     assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); | 
 |     const Expr *FirstArg = Call.getArgExpr(0); | 
 |     if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) | 
 |       return false; | 
 |     return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); | 
 |   } | 
 |  | 
 |   if (MakeUniqueVariants.contains(Call)) { | 
 |     if (!ModelSmartPtrDereference) | 
 |       return false; | 
 |  | 
 |     const std::optional<SVal> ThisRegionOpt = | 
 |         Call.getReturnValueUnderConstruction(); | 
 |     if (!ThisRegionOpt) | 
 |       return false; | 
 |  | 
 |     const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( | 
 |         Call.getCFGElementRef(), C.getLocationContext(), | 
 |         getPointerTypeFromTemplateArg(Call, C), C.blockCount()); | 
 |  | 
 |     const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); | 
 |     State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); | 
 |     State = State->assume(PtrVal, true); | 
 |  | 
 |     // TODO: ExprEngine should do this for us. | 
 |     // For a bit more context: | 
 |     // 1) Why do we need this? Since we are modelling a "function" | 
 |     // that returns a constructed object we need to store this information in | 
 |     // the program state. | 
 |     // | 
 |     // 2) Why does this work? | 
 |     // `updateObjectsUnderConstruction` does exactly as it sounds. | 
 |     // | 
 |     // 3) How should it look like when moved to the Engine? | 
 |     // It would be nice if we can just | 
 |     // pretend we don't need to know about this - ie, completely automatic work. | 
 |     // However, realistically speaking, I think we would need to "signal" the | 
 |     // ExprEngine evalCall handler that we are constructing an object with this | 
 |     // function call (constructors obviously construct, hence can be | 
 |     // automatically deduced). | 
 |     auto &Engine = State->getStateManager().getOwningEngine(); | 
 |     State = Engine.updateObjectsUnderConstruction( | 
 |         *ThisRegionOpt, nullptr, State, C.getLocationContext(), | 
 |         Call.getConstructionContext(), {}); | 
 |  | 
 |     // We don't leave a note here since it is guaranteed the | 
 |     // unique_ptr from this call is non-null (hence is safe to de-reference). | 
 |     C.addTransition(State); | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (!smartptr::isStdSmartPtrCall(Call)) | 
 |     return false; | 
 |  | 
 |   if (isBoolConversionMethod(Call)) { | 
 |     const MemRegion *ThisR = | 
 |         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); | 
 |  | 
 |     if (ModelSmartPtrDereference) { | 
 |       // The check for the region is moved is duplicated in handleBoolOperation | 
 |       // method. | 
 |       // FIXME: Once we model std::move for smart pointers clean up this and use | 
 |       // that modeling. | 
 |       handleBoolConversion(Call, C); | 
 |       return true; | 
 |     } else { | 
 |       if (!move::isMovedFrom(State, ThisR)) { | 
 |         // TODO: Model this case as well. At least, avoid invalidation of | 
 |         // globals. | 
 |         return false; | 
 |       } | 
 |  | 
 |       // TODO: Add a note to bug reports describing this decision. | 
 |       C.addTransition(State->BindExpr( | 
 |           Call.getOriginExpr(), C.getLocationContext(), | 
 |           C.getSValBuilder().makeZeroVal(Call.getResultType()))); | 
 |  | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   if (!ModelSmartPtrDereference) | 
 |     return false; | 
 |  | 
 |   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { | 
 |     if (CC->getDecl()->isCopyConstructor()) | 
 |       return false; | 
 |  | 
 |     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); | 
 |     if (!ThisRegion) | 
 |       return false; | 
 |  | 
 |     QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); | 
 |  | 
 |     if (CC->getDecl()->isMoveConstructor()) | 
 |       return handleMoveCtr(Call, C, ThisRegion); | 
 |  | 
 |     if (Call.getNumArgs() == 0) { | 
 |       auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); | 
 |       State = State->set<TrackedRegionMap>(ThisRegion, NullVal); | 
 |  | 
 |       C.addTransition( | 
 |           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, | 
 |                                            llvm::raw_ostream &OS) { | 
 |             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |                 !BR.isInteresting(ThisRegion)) | 
 |               return; | 
 |             OS << "Default constructed smart pointer"; | 
 |             checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |             OS << " is null"; | 
 |           })); | 
 |     } else { | 
 |       const auto *TrackingExpr = Call.getArgExpr(0); | 
 |       assert(TrackingExpr->getType()->isPointerType() && | 
 |              "Adding a non pointer value to TrackedRegionMap"); | 
 |       auto ArgVal = Call.getArgSVal(0); | 
 |       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); | 
 |  | 
 |       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, | 
 |                                            ArgVal](PathSensitiveBugReport &BR, | 
 |                                                    llvm::raw_ostream &OS) { | 
 |         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |             !BR.isInteresting(ThisRegion)) | 
 |           return; | 
 |         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); | 
 |         OS << "Smart pointer"; | 
 |         checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |         if (ArgVal.isZeroConstant()) | 
 |           OS << " is constructed using a null value"; | 
 |         else | 
 |           OS << " is constructed"; | 
 |       })); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (handleAssignOp(Call, C)) | 
 |     return true; | 
 |  | 
 |   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); | 
 |   if (!Handler) | 
 |     return false; | 
 |   (this->**Handler)(Call, C); | 
 |  | 
 |   return C.isDifferent(); | 
 | } | 
 |  | 
 | std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( | 
 |     ProgramStateRef State, const MemRegion *ThisRegion, ConstCFGElementRef Elem, | 
 |     QualType Type, CheckerContext &C) const { | 
 |   const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); | 
 |   if (Ptr) | 
 |     return {*Ptr, State}; | 
 |   auto Val = C.getSValBuilder().conjureSymbolVal(Elem, C.getLocationContext(), | 
 |                                                  Type, C.blockCount()); | 
 |   State = State->set<TrackedRegionMap>(ThisRegion, Val); | 
 |   return {Val, State}; | 
 | } | 
 |  | 
 | bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, | 
 |                                            CheckerContext &C) const { | 
 |   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); | 
 |   if (!FC) | 
 |     return false; | 
 |   const FunctionDecl *FD = FC->getDecl(); | 
 |   if (!FD->isOverloadedOperator()) | 
 |     return false; | 
 |   const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); | 
 |   if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || | 
 |         OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || | 
 |         OOK == OO_Spaceship)) | 
 |     return false; | 
 |  | 
 |   // There are some special cases about which we can infer about | 
 |   // the resulting answer. | 
 |   // For reference, there is a discussion at https://reviews.llvm.org/D104616. | 
 |   // Also, the cppreference page is good to look at | 
 |   // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. | 
 |  | 
 |   auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, | 
 |                                 ConstCFGElementRef Elem, | 
 |                                 SVal S) -> std::pair<SVal, ProgramStateRef> { | 
 |     if (S.isZeroConstant()) { | 
 |       return {S, State}; | 
 |     } | 
 |     const MemRegion *Reg = S.getAsRegion(); | 
 |     assert(Reg && | 
 |            "this pointer of std::unique_ptr should be obtainable as MemRegion"); | 
 |     QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); | 
 |     return retrieveOrConjureInnerPtrVal(State, Reg, Elem, Type, C); | 
 |   }; | 
 |  | 
 |   SVal First = Call.getArgSVal(0); | 
 |   SVal Second = Call.getArgSVal(1); | 
 |   const auto *FirstExpr = Call.getArgExpr(0); | 
 |   const auto *SecondExpr = Call.getArgExpr(1); | 
 |  | 
 |   const auto *ResultExpr = Call.getOriginExpr(); | 
 |   const auto *LCtx = C.getLocationContext(); | 
 |   auto &Bldr = C.getSValBuilder(); | 
 |   ProgramStateRef State = C.getState(); | 
 |  | 
 |   SVal FirstPtrVal, SecondPtrVal; | 
 |   std::tie(FirstPtrVal, State) = | 
 |       makeSValFor(State, FirstExpr, Call.getCFGElementRef(), First); | 
 |   std::tie(SecondPtrVal, State) = | 
 |       makeSValFor(State, SecondExpr, Call.getCFGElementRef(), Second); | 
 |   BinaryOperatorKind BOK = | 
 |       operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe(); | 
 |   auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal, | 
 |                                Call.getResultType()); | 
 |  | 
 |   if (OOK != OO_Spaceship) { | 
 |     ProgramStateRef TrueState, FalseState; | 
 |     std::tie(TrueState, FalseState) = | 
 |         State->assume(*RetVal.getAs<DefinedOrUnknownSVal>()); | 
 |     if (TrueState) | 
 |       C.addTransition( | 
 |           TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true))); | 
 |     if (FalseState) | 
 |       C.addTransition( | 
 |           FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false))); | 
 |   } else { | 
 |     C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal)); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call, | 
 |                                              CheckerContext &C) const { | 
 |   // operator<< does not modify the smart pointer. | 
 |   // And we don't really have much of modelling of basic_ostream. | 
 |   // So, we are better off: | 
 |   // 1) Invalidating the mem-region of the ostream object at hand. | 
 |   // 2) Setting the SVal of the basic_ostream as the return value. | 
 |   // Not very satisfying, but it gets the job done, and is better | 
 |   // than the default handling. :) | 
 |  | 
 |   ProgramStateRef State = C.getState(); | 
 |   const auto StreamVal = Call.getArgSVal(0); | 
 |   const MemRegion *StreamThisRegion = StreamVal.getAsRegion(); | 
 |   if (!StreamThisRegion) | 
 |     return false; | 
 |   State = | 
 |       State->invalidateRegions({StreamThisRegion}, Call.getCFGElementRef(), | 
 |                                C.blockCount(), C.getLocationContext(), false); | 
 |   State = | 
 |       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal); | 
 |   C.addTransition(State); | 
 |   return true; | 
 | } | 
 |  | 
 | void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, | 
 |                                         CheckerContext &C) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   // Clean up dead regions from the region map. | 
 |   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); | 
 |   for (auto E : TrackedRegions) { | 
 |     const MemRegion *Region = E.first; | 
 |     bool IsRegDead = !SymReaper.isLiveRegion(Region); | 
 |  | 
 |     if (IsRegDead) | 
 |       State = State->remove<TrackedRegionMap>(Region); | 
 |   } | 
 |   C.addTransition(State); | 
 | } | 
 |  | 
 | void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, | 
 |                                   const char *NL, const char *Sep) const { | 
 |   TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); | 
 |  | 
 |   if (!RS.isEmpty()) { | 
 |     Out << Sep << "Smart ptr regions :" << NL; | 
 |     for (auto I : RS) { | 
 |       I.first->dumpToStream(Out); | 
 |       if (smartptr::isNullSmartPtr(State, I.first)) | 
 |         Out << ": Null"; | 
 |       else | 
 |         Out << ": Non Null"; | 
 |       Out << NL; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | ProgramStateRef SmartPtrModeling::checkRegionChanges( | 
 |     ProgramStateRef State, const InvalidatedSymbols *Invalidated, | 
 |     ArrayRef<const MemRegion *> ExplicitRegions, | 
 |     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, | 
 |     const CallEvent *Call) const { | 
 |   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); | 
 |   TrackedRegionMapTy::Factory &RegionMapFactory = | 
 |       State->get_context<TrackedRegionMap>(); | 
 |   for (const auto *Region : Regions) | 
 |     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, | 
 |                                         Region->getBaseRegion()); | 
 |   return State->set<TrackedRegionMap>(RegionMap); | 
 | } | 
 |  | 
 | void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, | 
 |                                         SymbolReaper &SR) const { | 
 |   // Marking tracked symbols alive | 
 |   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); | 
 |   for (SVal Val : llvm::make_second_range(TrackedRegions)) { | 
 |     for (SymbolRef Sym : Val.symbols()) { | 
 |       SR.markLive(Sym); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SmartPtrModeling::handleReset(const CallEvent &Call, | 
 |                                    CheckerContext &C) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   const auto *IC = dyn_cast<CXXInstanceCall>(&Call); | 
 |   if (!IC) | 
 |     return; | 
 |  | 
 |   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); | 
 |   if (!ThisRegion) | 
 |     return; | 
 |  | 
 |   assert(Call.getArgExpr(0)->getType()->isPointerType() && | 
 |          "Adding a non pointer value to TrackedRegionMap"); | 
 |   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); | 
 |   const auto *TrackingExpr = Call.getArgExpr(0); | 
 |   C.addTransition( | 
 |       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, | 
 |                                                      llvm::raw_ostream &OS) { | 
 |         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |             !BR.isInteresting(ThisRegion)) | 
 |           return; | 
 |         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); | 
 |         OS << "Smart pointer"; | 
 |         checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |         OS << " reset using a null value"; | 
 |       })); | 
 |   // TODO: Make sure to ivalidate the region in the Store if we don't have | 
 |   // time to model all methods. | 
 | } | 
 |  | 
 | void SmartPtrModeling::handleRelease(const CallEvent &Call, | 
 |                                      CheckerContext &C) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   const auto *IC = dyn_cast<CXXInstanceCall>(&Call); | 
 |   if (!IC) | 
 |     return; | 
 |  | 
 |   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); | 
 |   if (!ThisRegion) | 
 |     return; | 
 |  | 
 |   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); | 
 |  | 
 |   if (InnerPointVal) { | 
 |     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), | 
 |                             *InnerPointVal); | 
 |   } | 
 |  | 
 |   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); | 
 |   auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType); | 
 |   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); | 
 |  | 
 |   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, | 
 |                                                    llvm::raw_ostream &OS) { | 
 |     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |         !BR.isInteresting(ThisRegion)) | 
 |       return; | 
 |  | 
 |     OS << "Smart pointer"; | 
 |     checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |     OS << " is released and set to null"; | 
 |   })); | 
 |   // TODO: Add support to enable MallocChecker to start tracking the raw | 
 |   // pointer. | 
 | } | 
 |  | 
 | void SmartPtrModeling::handleSwapMethod(const CallEvent &Call, | 
 |                                         CheckerContext &C) const { | 
 |   // To model unique_ptr::swap() method. | 
 |   const auto *IC = dyn_cast<CXXInstanceCall>(&Call); | 
 |   if (!IC) | 
 |     return; | 
 |  | 
 |   auto State = C.getState(); | 
 |   handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C); | 
 | } | 
 |  | 
 | bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First, | 
 |                                   SVal Second, CheckerContext &C) const { | 
 |   const MemRegion *FirstThisRegion = First.getAsRegion(); | 
 |   if (!FirstThisRegion) | 
 |     return false; | 
 |   const MemRegion *SecondThisRegion = Second.getAsRegion(); | 
 |   if (!SecondThisRegion) | 
 |     return false; | 
 |  | 
 |   const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion); | 
 |   const auto *SecondInnerPtrVal = | 
 |       State->get<TrackedRegionMap>(SecondThisRegion); | 
 |  | 
 |   State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal); | 
 |   State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal); | 
 |  | 
 |   C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion]( | 
 |                                           PathSensitiveBugReport &BR, | 
 |                                           llvm::raw_ostream &OS) { | 
 |     if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) | 
 |       return; | 
 |     if (BR.isInteresting(FirstThisRegion) && | 
 |         !BR.isInteresting(SecondThisRegion)) { | 
 |       BR.markInteresting(SecondThisRegion); | 
 |       BR.markNotInteresting(FirstThisRegion); | 
 |     } | 
 |     if (BR.isInteresting(SecondThisRegion) && | 
 |         !BR.isInteresting(FirstThisRegion)) { | 
 |       BR.markInteresting(FirstThisRegion); | 
 |       BR.markNotInteresting(SecondThisRegion); | 
 |     } | 
 |     // TODO: We need to emit some note here probably!! | 
 |   })); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void SmartPtrModeling::handleGet(const CallEvent &Call, | 
 |                                  CheckerContext &C) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   const auto *IC = dyn_cast<CXXInstanceCall>(&Call); | 
 |   if (!IC) | 
 |     return; | 
 |  | 
 |   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); | 
 |   if (!ThisRegion) | 
 |     return; | 
 |  | 
 |   SVal InnerPointerVal; | 
 |   std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal( | 
 |       State, ThisRegion, Call.getCFGElementRef(), Call.getResultType(), C); | 
 |   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), | 
 |                           InnerPointerVal); | 
 |   // TODO: Add NoteTag, for how the raw pointer got using 'get' method. | 
 |   C.addTransition(State); | 
 | } | 
 |  | 
 | bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, | 
 |                                       CheckerContext &C) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); | 
 |   if (!OC) | 
 |     return false; | 
 |   OverloadedOperatorKind OOK = OC->getOverloadedOperator(); | 
 |   if (OOK != OO_Equal) | 
 |     return false; | 
 |   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); | 
 |   if (!ThisRegion) | 
 |     return false; | 
 |  | 
 |   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); | 
 |  | 
 |   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); | 
 |   // In case of 'nullptr' or '0' assigned | 
 |   if (!OtherSmartPtrRegion) { | 
 |     bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); | 
 |     if (!AssignedNull) | 
 |       return false; | 
 |     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); | 
 |     State = State->set<TrackedRegionMap>(ThisRegion, NullVal); | 
 |     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, | 
 |                                                      llvm::raw_ostream &OS) { | 
 |       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |           !BR.isInteresting(ThisRegion)) | 
 |         return; | 
 |       OS << "Smart pointer"; | 
 |       checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |       OS << " is assigned to null"; | 
 |     })); | 
 |     return true; | 
 |   } | 
 |  | 
 |   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); | 
 | } | 
 |  | 
 | bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, | 
 |                                      const MemRegion *ThisRegion) const { | 
 |   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); | 
 |   if (!OtherSmartPtrRegion) | 
 |     return false; | 
 |  | 
 |   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); | 
 | } | 
 |  | 
 | bool SmartPtrModeling::updateMovedSmartPointers( | 
 |     CheckerContext &C, const MemRegion *ThisRegion, | 
 |     const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const { | 
 |   ProgramStateRef State = C.getState(); | 
 |   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); | 
 |   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); | 
 |   if (OtherInnerPtr) { | 
 |     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); | 
 |  | 
 |     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); | 
 |     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); | 
 |     bool IsArgValNull = OtherInnerPtr->isZeroConstant(); | 
 |  | 
 |     C.addTransition( | 
 |         State, | 
 |         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( | 
 |                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { | 
 |           if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) | 
 |             return; | 
 |           if (BR.isInteresting(OtherSmartPtrRegion)) { | 
 |             OS << "Smart pointer"; | 
 |             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); | 
 |             OS << " is null after being moved to"; | 
 |             checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |           } | 
 |           if (BR.isInteresting(ThisRegion) && IsArgValNull) { | 
 |             OS << "A null pointer value is moved to"; | 
 |             checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |             BR.markInteresting(OtherSmartPtrRegion); | 
 |           } | 
 |         })); | 
 |     return true; | 
 |   } else { | 
 |     // In case we dont know anything about value we are moving from | 
 |     // remove the entry from map for which smart pointer got moved to. | 
 |     // For unique_ptr<A>, Ty will be 'A*'. | 
 |     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); | 
 |     State = State->remove<TrackedRegionMap>(ThisRegion); | 
 |     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); | 
 |     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, | 
 |                                          ThisRegion](PathSensitiveBugReport &BR, | 
 |                                                      llvm::raw_ostream &OS) { | 
 |       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || | 
 |           !BR.isInteresting(OtherSmartPtrRegion)) | 
 |         return; | 
 |       OS << "Smart pointer"; | 
 |       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); | 
 |       OS << " is null after; previous value moved to"; | 
 |       checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |     })); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, | 
 |                                             CheckerContext &C) const { | 
 |   // To model unique_ptr::operator bool | 
 |   ProgramStateRef State = C.getState(); | 
 |   const Expr *CallExpr = Call.getOriginExpr(); | 
 |   const MemRegion *ThisRegion = | 
 |       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); | 
 |  | 
 |   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); | 
 |  | 
 |   SVal InnerPointerVal; | 
 |   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { | 
 |     InnerPointerVal = *InnerValPtr; | 
 |   } else { | 
 |     // In case of inner pointer SVal is not available we create | 
 |     // conjureSymbolVal for inner pointer value. | 
 |     auto InnerPointerType = getInnerPointerType(Call, C); | 
 |     if (InnerPointerType.isNull()) | 
 |       return; | 
 |  | 
 |     InnerPointerVal = C.getSValBuilder().conjureSymbolVal( | 
 |         Call, InnerPointerType, C.blockCount()); | 
 |     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); | 
 |   } | 
 |  | 
 |   if (State->isNull(InnerPointerVal).isConstrainedTrue()) { | 
 |     State = State->BindExpr(CallExpr, C.getLocationContext(), | 
 |                             C.getSValBuilder().makeTruthVal(false)); | 
 |  | 
 |     C.addTransition(State); | 
 |     return; | 
 |   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { | 
 |     State = State->BindExpr(CallExpr, C.getLocationContext(), | 
 |                             C.getSValBuilder().makeTruthVal(true)); | 
 |  | 
 |     C.addTransition(State); | 
 |     return; | 
 |   } else if (move::isMovedFrom(State, ThisRegion)) { | 
 |     C.addTransition( | 
 |         State->BindExpr(CallExpr, C.getLocationContext(), | 
 |                         C.getSValBuilder().makeZeroVal(Call.getResultType()))); | 
 |     return; | 
 |   } else { | 
 |     ProgramStateRef NotNullState, NullState; | 
 |     std::tie(NotNullState, NullState) = | 
 |         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); | 
 |  | 
 |     auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); | 
 |     // Explicitly tracking the region as null. | 
 |     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); | 
 |  | 
 |     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), | 
 |                                     C.getSValBuilder().makeTruthVal(false)); | 
 |     C.addTransition(NullState, C.getNoteTag( | 
 |                                    [ThisRegion](PathSensitiveBugReport &BR, | 
 |                                                 llvm::raw_ostream &OS) { | 
 |                                      OS << "Assuming smart pointer"; | 
 |                                      checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |                                      OS << " is null"; | 
 |                                    }, | 
 |                                    /*IsPrunable=*/true)); | 
 |     NotNullState = | 
 |         NotNullState->BindExpr(CallExpr, C.getLocationContext(), | 
 |                                C.getSValBuilder().makeTruthVal(true)); | 
 |     C.addTransition( | 
 |         NotNullState, | 
 |         C.getNoteTag( | 
 |             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { | 
 |               OS << "Assuming smart pointer"; | 
 |               checkAndPrettyPrintRegion(OS, ThisRegion); | 
 |               OS << " is non-null"; | 
 |             }, | 
 |             /*IsPrunable=*/true)); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void ento::registerSmartPtrModeling(CheckerManager &Mgr) { | 
 |   auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); | 
 |   Checker->ModelSmartPtrDereference = | 
 |       Mgr.getAnalyzerOptions().getCheckerBooleanOption( | 
 |           Checker, "ModelSmartPtrDereference"); | 
 | } | 
 |  | 
 | bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { | 
 |   const LangOptions &LO = mgr.getLangOpts(); | 
 |   return LO.CPlusPlus; | 
 | } |