10b57cec5SDimitry Andric // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines a checker that models various aspects of 100b57cec5SDimitry Andric // C++ smart pointer behavior. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "Move.h" 155ffd83dbSDimitry Andric #include "SmartPtr.h" 160b57cec5SDimitry Andric 175ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h" 18e8d8bef9SDimitry Andric #include "clang/AST/DeclarationName.h" 190b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h" 205ffd83dbSDimitry Andric #include "clang/AST/Type.h" 21e8d8bef9SDimitry Andric #include "clang/Basic/LLVM.h" 220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 26*349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 29fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 30e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 315ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 325ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 33e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 34fe6060f1SDimitry Andric #include "llvm/ADT/StringMap.h" 35fe6060f1SDimitry Andric #include "llvm/Support/ErrorHandling.h" 36e8d8bef9SDimitry Andric #include <string> 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric using namespace clang; 390b57cec5SDimitry Andric using namespace ento; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric namespace { 42fe6060f1SDimitry Andric 43e8d8bef9SDimitry Andric class SmartPtrModeling 44e8d8bef9SDimitry Andric : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, 45e8d8bef9SDimitry Andric check::LiveSymbols> { 465ffd83dbSDimitry Andric 47e8d8bef9SDimitry Andric bool isBoolConversionMethod(const CallEvent &Call) const; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric public: 505ffd83dbSDimitry Andric // Whether the checker should model for null dereferences of smart pointers. 515ffd83dbSDimitry Andric DefaultBool ModelSmartPtrDereference; 520b57cec5SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 535ffd83dbSDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 545ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 55e8d8bef9SDimitry Andric ProgramStateRef 56e8d8bef9SDimitry Andric checkRegionChanges(ProgramStateRef State, 57e8d8bef9SDimitry Andric const InvalidatedSymbols *Invalidated, 58e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 59e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, 60e8d8bef9SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const; 61e8d8bef9SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 62e8d8bef9SDimitry Andric const char *Sep) const override; 63e8d8bef9SDimitry Andric void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 645ffd83dbSDimitry Andric 655ffd83dbSDimitry Andric private: 665ffd83dbSDimitry Andric void handleReset(const CallEvent &Call, CheckerContext &C) const; 675ffd83dbSDimitry Andric void handleRelease(const CallEvent &Call, CheckerContext &C) const; 68fe6060f1SDimitry Andric void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; 69e8d8bef9SDimitry Andric void handleGet(const CallEvent &Call, CheckerContext &C) const; 70e8d8bef9SDimitry Andric bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; 71e8d8bef9SDimitry Andric bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, 72e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const; 73e8d8bef9SDimitry Andric bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, 74e8d8bef9SDimitry Andric const MemRegion *OtherSmartPtrRegion) const; 75e8d8bef9SDimitry Andric void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; 76fe6060f1SDimitry Andric bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; 77fe6060f1SDimitry Andric bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; 78fe6060f1SDimitry Andric bool handleSwap(ProgramStateRef State, SVal First, SVal Second, 79fe6060f1SDimitry Andric CheckerContext &C) const; 80fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> 81fe6060f1SDimitry Andric retrieveOrConjureInnerPtrVal(ProgramStateRef State, 82fe6060f1SDimitry Andric const MemRegion *ThisRegion, const Expr *E, 83fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const; 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric using SmartPtrMethodHandlerFn = 865ffd83dbSDimitry Andric void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; 875ffd83dbSDimitry Andric CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ 885ffd83dbSDimitry Andric {{"reset"}, &SmartPtrModeling::handleReset}, 895ffd83dbSDimitry Andric {{"release"}, &SmartPtrModeling::handleRelease}, 90fe6060f1SDimitry Andric {{"swap", 1}, &SmartPtrModeling::handleSwapMethod}, 91e8d8bef9SDimitry Andric {{"get"}, &SmartPtrModeling::handleGet}}; 92fe6060f1SDimitry Andric const CallDescription StdSwapCall{{"std", "swap"}, 2}; 93fe6060f1SDimitry Andric const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; 94fe6060f1SDimitry Andric const CallDescription StdMakeUniqueForOverwriteCall{ 95fe6060f1SDimitry Andric {"std", "make_unique_for_overwrite"}}; 960b57cec5SDimitry Andric }; 970b57cec5SDimitry Andric } // end of anonymous namespace 980b57cec5SDimitry Andric 995ffd83dbSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 1005ffd83dbSDimitry Andric 101fe6060f1SDimitry Andric // Checks if RD has name in Names and is in std namespace 102fe6060f1SDimitry Andric static bool hasStdClassWithName(const CXXRecordDecl *RD, 103fe6060f1SDimitry Andric ArrayRef<llvm::StringLiteral> Names) { 104fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 105fe6060f1SDimitry Andric return false; 106*349cc55cSDimitry Andric if (RD->getDeclName().isIdentifier()) 107*349cc55cSDimitry Andric return llvm::is_contained(Names, RD->getName()); 108fe6060f1SDimitry Andric return false; 109fe6060f1SDimitry Andric } 110fe6060f1SDimitry Andric 111fe6060f1SDimitry Andric constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr", 112fe6060f1SDimitry Andric "weak_ptr"}; 113fe6060f1SDimitry Andric 114fe6060f1SDimitry Andric static bool isStdSmartPtr(const CXXRecordDecl *RD) { 115fe6060f1SDimitry Andric return hasStdClassWithName(RD, STD_PTR_NAMES); 116fe6060f1SDimitry Andric } 117fe6060f1SDimitry Andric 118fe6060f1SDimitry Andric static bool isStdSmartPtr(const Expr *E) { 119fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 120fe6060f1SDimitry Andric } 121fe6060f1SDimitry Andric 1225ffd83dbSDimitry Andric // Define the inter-checker API. 1235ffd83dbSDimitry Andric namespace clang { 1245ffd83dbSDimitry Andric namespace ento { 1255ffd83dbSDimitry Andric namespace smartptr { 1265ffd83dbSDimitry Andric bool isStdSmartPtrCall(const CallEvent &Call) { 1275ffd83dbSDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 1285ffd83dbSDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 1295ffd83dbSDimitry Andric return false; 130fe6060f1SDimitry Andric return isStdSmartPtr(MethodDecl->getParent()); 131fe6060f1SDimitry Andric } 1325ffd83dbSDimitry Andric 133fe6060f1SDimitry Andric bool isStdSmartPtr(const CXXRecordDecl *RD) { 134fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 1355ffd83dbSDimitry Andric return false; 1365ffd83dbSDimitry Andric 137fe6060f1SDimitry Andric if (RD->getDeclName().isIdentifier()) { 138fe6060f1SDimitry Andric StringRef Name = RD->getName(); 1395ffd83dbSDimitry Andric return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 1405ffd83dbSDimitry Andric } 1415ffd83dbSDimitry Andric return false; 1425ffd83dbSDimitry Andric } 1435ffd83dbSDimitry Andric 144fe6060f1SDimitry Andric bool isStdSmartPtr(const Expr *E) { 145fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 146fe6060f1SDimitry Andric } 147fe6060f1SDimitry Andric 1485ffd83dbSDimitry Andric bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 1495ffd83dbSDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 150e8d8bef9SDimitry Andric return InnerPointVal && 151e8d8bef9SDimitry Andric !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); 1525ffd83dbSDimitry Andric } 1535ffd83dbSDimitry Andric } // namespace smartptr 1545ffd83dbSDimitry Andric } // namespace ento 1555ffd83dbSDimitry Andric } // namespace clang 1565ffd83dbSDimitry Andric 157e8d8bef9SDimitry Andric // If a region is removed all of the subregions need to be removed too. 158e8d8bef9SDimitry Andric static TrackedRegionMapTy 159e8d8bef9SDimitry Andric removeTrackedSubregions(TrackedRegionMapTy RegionMap, 160e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory, 161e8d8bef9SDimitry Andric const MemRegion *Region) { 162e8d8bef9SDimitry Andric if (!Region) 163e8d8bef9SDimitry Andric return RegionMap; 164e8d8bef9SDimitry Andric for (const auto &E : RegionMap) { 165e8d8bef9SDimitry Andric if (E.first->isSubRegionOf(Region)) 166e8d8bef9SDimitry Andric RegionMap = RegionMapFactory.remove(RegionMap, E.first); 167e8d8bef9SDimitry Andric } 168e8d8bef9SDimitry Andric return RegionMap; 169e8d8bef9SDimitry Andric } 170e8d8bef9SDimitry Andric 171e8d8bef9SDimitry Andric static ProgramStateRef updateSwappedRegion(ProgramStateRef State, 172e8d8bef9SDimitry Andric const MemRegion *Region, 173e8d8bef9SDimitry Andric const SVal *RegionInnerPointerVal) { 174e8d8bef9SDimitry Andric if (RegionInnerPointerVal) { 175e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); 176e8d8bef9SDimitry Andric } else { 177e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(Region); 178e8d8bef9SDimitry Andric } 179e8d8bef9SDimitry Andric return State; 180e8d8bef9SDimitry Andric } 181e8d8bef9SDimitry Andric 182fe6060f1SDimitry Andric static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { 183fe6060f1SDimitry Andric if (!RD || !RD->isInStdNamespace()) 184fe6060f1SDimitry Andric return {}; 185fe6060f1SDimitry Andric 186fe6060f1SDimitry Andric const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); 187fe6060f1SDimitry Andric if (!TSD) 188fe6060f1SDimitry Andric return {}; 189fe6060f1SDimitry Andric 190fe6060f1SDimitry Andric auto TemplateArgs = TSD->getTemplateArgs().asArray(); 191fe6060f1SDimitry Andric if (TemplateArgs.empty()) 192fe6060f1SDimitry Andric return {}; 193fe6060f1SDimitry Andric auto InnerValueType = TemplateArgs[0].getAsType(); 194fe6060f1SDimitry Andric return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); 195fe6060f1SDimitry Andric } 196fe6060f1SDimitry Andric 197fe6060f1SDimitry Andric // This is for use with standalone-functions like std::make_unique, 198fe6060f1SDimitry Andric // std::make_unique_for_overwrite, etc. It reads the template parameter and 199fe6060f1SDimitry Andric // returns the pointer type corresponding to it, 200fe6060f1SDimitry Andric static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, 201fe6060f1SDimitry Andric CheckerContext &C) { 202fe6060f1SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 203fe6060f1SDimitry Andric if (!FD || !FD->isFunctionTemplateSpecialization()) 204fe6060f1SDimitry Andric return {}; 205fe6060f1SDimitry Andric const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); 206fe6060f1SDimitry Andric if (TemplateArgs.size() == 0) 207fe6060f1SDimitry Andric return {}; 208fe6060f1SDimitry Andric auto ValueType = TemplateArgs[0].getAsType(); 209fe6060f1SDimitry Andric return C.getASTContext().getPointerType(ValueType.getCanonicalType()); 210fe6060f1SDimitry Andric } 211fe6060f1SDimitry Andric 212e8d8bef9SDimitry Andric // Helper method to get the inner pointer type of specialized smart pointer 213e8d8bef9SDimitry Andric // Returns empty type if not found valid inner pointer type. 214e8d8bef9SDimitry Andric static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { 215e8d8bef9SDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 216e8d8bef9SDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 217e8d8bef9SDimitry Andric return {}; 218e8d8bef9SDimitry Andric 219e8d8bef9SDimitry Andric const auto *RecordDecl = MethodDecl->getParent(); 220fe6060f1SDimitry Andric return getInnerPointerType(C, RecordDecl); 221e8d8bef9SDimitry Andric } 222e8d8bef9SDimitry Andric 223e8d8bef9SDimitry Andric // Helper method to pretty print region and avoid extra spacing. 224e8d8bef9SDimitry Andric static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, 225e8d8bef9SDimitry Andric const MemRegion *Region) { 226e8d8bef9SDimitry Andric if (Region->canPrintPretty()) { 227e8d8bef9SDimitry Andric OS << " "; 228e8d8bef9SDimitry Andric Region->printPretty(OS); 229e8d8bef9SDimitry Andric } 230e8d8bef9SDimitry Andric } 231e8d8bef9SDimitry Andric 232e8d8bef9SDimitry Andric bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { 2330b57cec5SDimitry Andric // TODO: Update CallDescription to support anonymous calls? 2340b57cec5SDimitry Andric // TODO: Handle other methods, such as .get() or .release(). 2350b57cec5SDimitry Andric // But once we do, we'd need a visitor to explain null dereferences 2360b57cec5SDimitry Andric // that are found via such modeling. 2370b57cec5SDimitry Andric const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 2380b57cec5SDimitry Andric return CD && CD->getConversionType()->isBooleanType(); 2390b57cec5SDimitry Andric } 2400b57cec5SDimitry Andric 241fe6060f1SDimitry Andric constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"}; 242fe6060f1SDimitry Andric 243fe6060f1SDimitry Andric bool isStdBasicOstream(const Expr *E) { 244fe6060f1SDimitry Andric const auto *RD = E->getType()->getAsCXXRecordDecl(); 245fe6060f1SDimitry Andric return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES); 246fe6060f1SDimitry Andric } 247fe6060f1SDimitry Andric 248fe6060f1SDimitry Andric static bool isStdFunctionCall(const CallEvent &Call) { 249fe6060f1SDimitry Andric return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace(); 250fe6060f1SDimitry Andric } 251fe6060f1SDimitry Andric 252fe6060f1SDimitry Andric bool isStdOstreamOperatorCall(const CallEvent &Call) { 253fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 254fe6060f1SDimitry Andric return false; 255fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 256fe6060f1SDimitry Andric if (!FC) 257fe6060f1SDimitry Andric return false; 258fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 259fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 260fe6060f1SDimitry Andric return false; 261fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 262fe6060f1SDimitry Andric if (OOK != clang::OO_LessLess) 263fe6060f1SDimitry Andric return false; 264fe6060f1SDimitry Andric return isStdSmartPtr(Call.getArgExpr(1)) && 265fe6060f1SDimitry Andric isStdBasicOstream(Call.getArgExpr(0)); 266fe6060f1SDimitry Andric } 267fe6060f1SDimitry Andric 268fe6060f1SDimitry Andric static bool isPotentiallyComparisionOpCall(const CallEvent &Call) { 269fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 270fe6060f1SDimitry Andric return false; 271fe6060f1SDimitry Andric return smartptr::isStdSmartPtr(Call.getArgExpr(0)) || 272fe6060f1SDimitry Andric smartptr::isStdSmartPtr(Call.getArgExpr(1)); 273fe6060f1SDimitry Andric } 274fe6060f1SDimitry Andric 2750b57cec5SDimitry Andric bool SmartPtrModeling::evalCall(const CallEvent &Call, 2760b57cec5SDimitry Andric CheckerContext &C) const { 277fe6060f1SDimitry Andric 278e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 279fe6060f1SDimitry Andric 280fe6060f1SDimitry Andric // If any one of the arg is a unique_ptr, then 281fe6060f1SDimitry Andric // we can try this function 282fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call)) 283fe6060f1SDimitry Andric if (handleComparisionOp(Call, C)) 284fe6060f1SDimitry Andric return true; 285fe6060f1SDimitry Andric 286fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) 287fe6060f1SDimitry Andric return handleOstreamOperator(Call, C); 288fe6060f1SDimitry Andric 289*349cc55cSDimitry Andric if (StdSwapCall.matches(Call)) { 290fe6060f1SDimitry Andric // Check the first arg, if it is of std::unique_ptr type. 291fe6060f1SDimitry Andric assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); 292fe6060f1SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0); 293fe6060f1SDimitry Andric if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) 294fe6060f1SDimitry Andric return false; 295fe6060f1SDimitry Andric return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); 296fe6060f1SDimitry Andric } 297fe6060f1SDimitry Andric 298*349cc55cSDimitry Andric if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { 299fe6060f1SDimitry Andric if (!ModelSmartPtrDereference) 300fe6060f1SDimitry Andric return false; 301fe6060f1SDimitry Andric 302fe6060f1SDimitry Andric const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); 303fe6060f1SDimitry Andric if (!ThisRegionOpt) 304fe6060f1SDimitry Andric return false; 305fe6060f1SDimitry Andric 306fe6060f1SDimitry Andric const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( 307fe6060f1SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 308fe6060f1SDimitry Andric getPointerTypeFromTemplateArg(Call, C), C.blockCount()); 309fe6060f1SDimitry Andric 310fe6060f1SDimitry Andric const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); 311fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); 312fe6060f1SDimitry Andric State = State->assume(PtrVal, true); 313fe6060f1SDimitry Andric 314fe6060f1SDimitry Andric // TODO: ExprEngine should do this for us. 315fe6060f1SDimitry Andric // For a bit more context: 316fe6060f1SDimitry Andric // 1) Why do we need this? Since we are modelling a "function" 317fe6060f1SDimitry Andric // that returns a constructed object we need to store this information in 318fe6060f1SDimitry Andric // the program state. 319fe6060f1SDimitry Andric // 320fe6060f1SDimitry Andric // 2) Why does this work? 321fe6060f1SDimitry Andric // `updateObjectsUnderConstruction` does exactly as it sounds. 322fe6060f1SDimitry Andric // 323fe6060f1SDimitry Andric // 3) How should it look like when moved to the Engine? 324fe6060f1SDimitry Andric // It would be nice if we can just 325fe6060f1SDimitry Andric // pretend we don't need to know about this - ie, completely automatic work. 326fe6060f1SDimitry Andric // However, realistically speaking, I think we would need to "signal" the 327fe6060f1SDimitry Andric // ExprEngine evalCall handler that we are constructing an object with this 328fe6060f1SDimitry Andric // function call (constructors obviously construct, hence can be 329fe6060f1SDimitry Andric // automatically deduced). 330fe6060f1SDimitry Andric auto &Engine = State->getStateManager().getOwningEngine(); 331fe6060f1SDimitry Andric State = Engine.updateObjectsUnderConstruction( 332fe6060f1SDimitry Andric *ThisRegionOpt, nullptr, State, C.getLocationContext(), 333fe6060f1SDimitry Andric Call.getConstructionContext(), {}); 334fe6060f1SDimitry Andric 335fe6060f1SDimitry Andric // We don't leave a note here since it is guaranteed the 336fe6060f1SDimitry Andric // unique_ptr from this call is non-null (hence is safe to de-reference). 337fe6060f1SDimitry Andric C.addTransition(State); 338fe6060f1SDimitry Andric return true; 339fe6060f1SDimitry Andric } 340fe6060f1SDimitry Andric 3415ffd83dbSDimitry Andric if (!smartptr::isStdSmartPtrCall(Call)) 3420b57cec5SDimitry Andric return false; 3430b57cec5SDimitry Andric 344e8d8bef9SDimitry Andric if (isBoolConversionMethod(Call)) { 3450b57cec5SDimitry Andric const MemRegion *ThisR = 3460b57cec5SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 3470b57cec5SDimitry Andric 348e8d8bef9SDimitry Andric if (ModelSmartPtrDereference) { 349e8d8bef9SDimitry Andric // The check for the region is moved is duplicated in handleBoolOperation 350e8d8bef9SDimitry Andric // method. 351e8d8bef9SDimitry Andric // FIXME: Once we model std::move for smart pointers clean up this and use 352e8d8bef9SDimitry Andric // that modeling. 353e8d8bef9SDimitry Andric handleBoolConversion(Call, C); 354e8d8bef9SDimitry Andric return true; 355e8d8bef9SDimitry Andric } else { 3560b57cec5SDimitry Andric if (!move::isMovedFrom(State, ThisR)) { 357e8d8bef9SDimitry Andric // TODO: Model this case as well. At least, avoid invalidation of 358e8d8bef9SDimitry Andric // globals. 3590b57cec5SDimitry Andric return false; 3600b57cec5SDimitry Andric } 3610b57cec5SDimitry Andric 3620b57cec5SDimitry Andric // TODO: Add a note to bug reports describing this decision. 363e8d8bef9SDimitry Andric C.addTransition(State->BindExpr( 364e8d8bef9SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 3650b57cec5SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType()))); 366e8d8bef9SDimitry Andric 3670b57cec5SDimitry Andric return true; 3680b57cec5SDimitry Andric } 369e8d8bef9SDimitry Andric } 3700b57cec5SDimitry Andric 3715ffd83dbSDimitry Andric if (!ModelSmartPtrDereference) 3725ffd83dbSDimitry Andric return false; 3735ffd83dbSDimitry Andric 3745ffd83dbSDimitry Andric if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 375e8d8bef9SDimitry Andric if (CC->getDecl()->isCopyConstructor()) 3765ffd83dbSDimitry Andric return false; 3775ffd83dbSDimitry Andric 378e8d8bef9SDimitry Andric const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 379e8d8bef9SDimitry Andric if (!ThisRegion) 3805ffd83dbSDimitry Andric return false; 3815ffd83dbSDimitry Andric 382e8d8bef9SDimitry Andric if (CC->getDecl()->isMoveConstructor()) 383e8d8bef9SDimitry Andric return handleMoveCtr(Call, C, ThisRegion); 384e8d8bef9SDimitry Andric 385e8d8bef9SDimitry Andric if (Call.getNumArgs() == 0) { 386e8d8bef9SDimitry Andric auto NullVal = C.getSValBuilder().makeNull(); 387e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 388e8d8bef9SDimitry Andric 389e8d8bef9SDimitry Andric C.addTransition( 390e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 391e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 392e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 393e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 394e8d8bef9SDimitry Andric return; 395e8d8bef9SDimitry Andric OS << "Default constructed smart pointer"; 396e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 397e8d8bef9SDimitry Andric OS << " is null"; 398e8d8bef9SDimitry Andric })); 399e8d8bef9SDimitry Andric } else { 400e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0); 401e8d8bef9SDimitry Andric assert(TrackingExpr->getType()->isPointerType() && 402e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap"); 403e8d8bef9SDimitry Andric auto ArgVal = Call.getArgSVal(0); 404e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 405e8d8bef9SDimitry Andric 406e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 407e8d8bef9SDimitry Andric ArgVal](PathSensitiveBugReport &BR, 408e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 409e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 410e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 411e8d8bef9SDimitry Andric return; 412e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 413e8d8bef9SDimitry Andric OS << "Smart pointer"; 414e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 415e8d8bef9SDimitry Andric if (ArgVal.isZeroConstant()) 416e8d8bef9SDimitry Andric OS << " is constructed using a null value"; 417e8d8bef9SDimitry Andric else 418e8d8bef9SDimitry Andric OS << " is constructed"; 419e8d8bef9SDimitry Andric })); 420e8d8bef9SDimitry Andric } 4215ffd83dbSDimitry Andric return true; 4220b57cec5SDimitry Andric } 4230b57cec5SDimitry Andric 424e8d8bef9SDimitry Andric if (handleAssignOp(Call, C)) 425e8d8bef9SDimitry Andric return true; 426e8d8bef9SDimitry Andric 4275ffd83dbSDimitry Andric const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 4285ffd83dbSDimitry Andric if (!Handler) 4295ffd83dbSDimitry Andric return false; 4305ffd83dbSDimitry Andric (this->**Handler)(Call, C); 4315ffd83dbSDimitry Andric 4325ffd83dbSDimitry Andric return C.isDifferent(); 4335ffd83dbSDimitry Andric } 4345ffd83dbSDimitry Andric 435fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( 436fe6060f1SDimitry Andric ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E, 437fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const { 438fe6060f1SDimitry Andric const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); 439fe6060f1SDimitry Andric if (Ptr) 440fe6060f1SDimitry Andric return {*Ptr, State}; 441fe6060f1SDimitry Andric auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(), 442fe6060f1SDimitry Andric Type, C.blockCount()); 443fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Val); 444fe6060f1SDimitry Andric return {Val, State}; 445fe6060f1SDimitry Andric } 446fe6060f1SDimitry Andric 447fe6060f1SDimitry Andric bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, 448fe6060f1SDimitry Andric CheckerContext &C) const { 449fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 450fe6060f1SDimitry Andric if (!FC) 451fe6060f1SDimitry Andric return false; 452fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 453fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 454fe6060f1SDimitry Andric return false; 455fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 456fe6060f1SDimitry Andric if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || 457fe6060f1SDimitry Andric OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || 458fe6060f1SDimitry Andric OOK == OO_Spaceship)) 459fe6060f1SDimitry Andric return false; 460fe6060f1SDimitry Andric 461fe6060f1SDimitry Andric // There are some special cases about which we can infer about 462fe6060f1SDimitry Andric // the resulting answer. 463fe6060f1SDimitry Andric // For reference, there is a discussion at https://reviews.llvm.org/D104616. 464fe6060f1SDimitry Andric // Also, the cppreference page is good to look at 465fe6060f1SDimitry Andric // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. 466fe6060f1SDimitry Andric 467fe6060f1SDimitry Andric auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, 468fe6060f1SDimitry Andric SVal S) -> std::pair<SVal, ProgramStateRef> { 469fe6060f1SDimitry Andric if (S.isZeroConstant()) { 470fe6060f1SDimitry Andric return {S, State}; 471fe6060f1SDimitry Andric } 472fe6060f1SDimitry Andric const MemRegion *Reg = S.getAsRegion(); 473fe6060f1SDimitry Andric assert(Reg && 474fe6060f1SDimitry Andric "this pointer of std::unique_ptr should be obtainable as MemRegion"); 475fe6060f1SDimitry Andric QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); 476fe6060f1SDimitry Andric return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C); 477fe6060f1SDimitry Andric }; 478fe6060f1SDimitry Andric 479fe6060f1SDimitry Andric SVal First = Call.getArgSVal(0); 480fe6060f1SDimitry Andric SVal Second = Call.getArgSVal(1); 481fe6060f1SDimitry Andric const auto *FirstExpr = Call.getArgExpr(0); 482fe6060f1SDimitry Andric const auto *SecondExpr = Call.getArgExpr(1); 483fe6060f1SDimitry Andric 484fe6060f1SDimitry Andric const auto *ResultExpr = Call.getOriginExpr(); 485fe6060f1SDimitry Andric const auto *LCtx = C.getLocationContext(); 486fe6060f1SDimitry Andric auto &Bldr = C.getSValBuilder(); 487fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 488fe6060f1SDimitry Andric 489fe6060f1SDimitry Andric SVal FirstPtrVal, SecondPtrVal; 490fe6060f1SDimitry Andric std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First); 491fe6060f1SDimitry Andric std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second); 492fe6060f1SDimitry Andric BinaryOperatorKind BOK = 493fe6060f1SDimitry Andric operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe(); 494fe6060f1SDimitry Andric auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal, 495fe6060f1SDimitry Andric Call.getResultType()); 496fe6060f1SDimitry Andric 497fe6060f1SDimitry Andric if (OOK != OO_Spaceship) { 498fe6060f1SDimitry Andric ProgramStateRef TrueState, FalseState; 499fe6060f1SDimitry Andric std::tie(TrueState, FalseState) = 500fe6060f1SDimitry Andric State->assume(*RetVal.getAs<DefinedOrUnknownSVal>()); 501fe6060f1SDimitry Andric if (TrueState) 502fe6060f1SDimitry Andric C.addTransition( 503fe6060f1SDimitry Andric TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true))); 504fe6060f1SDimitry Andric if (FalseState) 505fe6060f1SDimitry Andric C.addTransition( 506fe6060f1SDimitry Andric FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false))); 507fe6060f1SDimitry Andric } else { 508fe6060f1SDimitry Andric C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal)); 509fe6060f1SDimitry Andric } 510fe6060f1SDimitry Andric return true; 511fe6060f1SDimitry Andric } 512fe6060f1SDimitry Andric 513fe6060f1SDimitry Andric bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call, 514fe6060f1SDimitry Andric CheckerContext &C) const { 515fe6060f1SDimitry Andric // operator<< does not modify the smart pointer. 516fe6060f1SDimitry Andric // And we don't really have much of modelling of basic_ostream. 517fe6060f1SDimitry Andric // So, we are better off: 518fe6060f1SDimitry Andric // 1) Invalidating the mem-region of the ostream object at hand. 519fe6060f1SDimitry Andric // 2) Setting the SVal of the basic_ostream as the return value. 520fe6060f1SDimitry Andric // Not very satisfying, but it gets the job done, and is better 521fe6060f1SDimitry Andric // than the default handling. :) 522fe6060f1SDimitry Andric 523fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 524fe6060f1SDimitry Andric const auto StreamVal = Call.getArgSVal(0); 525fe6060f1SDimitry Andric const MemRegion *StreamThisRegion = StreamVal.getAsRegion(); 526fe6060f1SDimitry Andric if (!StreamThisRegion) 527fe6060f1SDimitry Andric return false; 528fe6060f1SDimitry Andric State = 529fe6060f1SDimitry Andric State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(), 530fe6060f1SDimitry Andric C.blockCount(), C.getLocationContext(), false); 531fe6060f1SDimitry Andric State = 532fe6060f1SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal); 533fe6060f1SDimitry Andric C.addTransition(State); 534fe6060f1SDimitry Andric return true; 535fe6060f1SDimitry Andric } 536fe6060f1SDimitry Andric 5375ffd83dbSDimitry Andric void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 5385ffd83dbSDimitry Andric CheckerContext &C) const { 5395ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 5405ffd83dbSDimitry Andric // Clean up dead regions from the region map. 5415ffd83dbSDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 5425ffd83dbSDimitry Andric for (auto E : TrackedRegions) { 5435ffd83dbSDimitry Andric const MemRegion *Region = E.first; 5445ffd83dbSDimitry Andric bool IsRegDead = !SymReaper.isLiveRegion(Region); 5455ffd83dbSDimitry Andric 5465ffd83dbSDimitry Andric if (IsRegDead) 5475ffd83dbSDimitry Andric State = State->remove<TrackedRegionMap>(Region); 5485ffd83dbSDimitry Andric } 5495ffd83dbSDimitry Andric C.addTransition(State); 5505ffd83dbSDimitry Andric } 5515ffd83dbSDimitry Andric 552e8d8bef9SDimitry Andric void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, 553e8d8bef9SDimitry Andric const char *NL, const char *Sep) const { 554e8d8bef9SDimitry Andric TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 555e8d8bef9SDimitry Andric 556e8d8bef9SDimitry Andric if (!RS.isEmpty()) { 557e8d8bef9SDimitry Andric Out << Sep << "Smart ptr regions :" << NL; 558e8d8bef9SDimitry Andric for (auto I : RS) { 559e8d8bef9SDimitry Andric I.first->dumpToStream(Out); 560e8d8bef9SDimitry Andric if (smartptr::isNullSmartPtr(State, I.first)) 561e8d8bef9SDimitry Andric Out << ": Null"; 562e8d8bef9SDimitry Andric else 563e8d8bef9SDimitry Andric Out << ": Non Null"; 564e8d8bef9SDimitry Andric Out << NL; 565e8d8bef9SDimitry Andric } 566e8d8bef9SDimitry Andric } 567e8d8bef9SDimitry Andric } 568e8d8bef9SDimitry Andric 569e8d8bef9SDimitry Andric ProgramStateRef SmartPtrModeling::checkRegionChanges( 570e8d8bef9SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated, 571e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 572e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 573e8d8bef9SDimitry Andric const CallEvent *Call) const { 574e8d8bef9SDimitry Andric TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); 575e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory = 576e8d8bef9SDimitry Andric State->get_context<TrackedRegionMap>(); 577e8d8bef9SDimitry Andric for (const auto *Region : Regions) 578e8d8bef9SDimitry Andric RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, 579e8d8bef9SDimitry Andric Region->getBaseRegion()); 580e8d8bef9SDimitry Andric return State->set<TrackedRegionMap>(RegionMap); 581e8d8bef9SDimitry Andric } 582e8d8bef9SDimitry Andric 583e8d8bef9SDimitry Andric void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, 584e8d8bef9SDimitry Andric SymbolReaper &SR) const { 585e8d8bef9SDimitry Andric // Marking tracked symbols alive 586e8d8bef9SDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 587e8d8bef9SDimitry Andric for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { 588e8d8bef9SDimitry Andric SVal Val = I->second; 589e8d8bef9SDimitry Andric for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { 590e8d8bef9SDimitry Andric SR.markLive(*si); 591e8d8bef9SDimitry Andric } 592e8d8bef9SDimitry Andric } 593e8d8bef9SDimitry Andric } 594e8d8bef9SDimitry Andric 5955ffd83dbSDimitry Andric void SmartPtrModeling::handleReset(const CallEvent &Call, 5965ffd83dbSDimitry Andric CheckerContext &C) const { 597e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 5985ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 5995ffd83dbSDimitry Andric if (!IC) 6005ffd83dbSDimitry Andric return; 6015ffd83dbSDimitry Andric 602e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 603e8d8bef9SDimitry Andric if (!ThisRegion) 6045ffd83dbSDimitry Andric return; 605e8d8bef9SDimitry Andric 606e8d8bef9SDimitry Andric assert(Call.getArgExpr(0)->getType()->isPointerType() && 607e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap"); 608e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); 609e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0); 610e8d8bef9SDimitry Andric C.addTransition( 611e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, 612e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 613e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 614e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 615e8d8bef9SDimitry Andric return; 616e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 617e8d8bef9SDimitry Andric OS << "Smart pointer"; 618e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 619e8d8bef9SDimitry Andric OS << " reset using a null value"; 620e8d8bef9SDimitry Andric })); 621e8d8bef9SDimitry Andric // TODO: Make sure to ivalidate the region in the Store if we don't have 6225ffd83dbSDimitry Andric // time to model all methods. 6235ffd83dbSDimitry Andric } 6245ffd83dbSDimitry Andric 6255ffd83dbSDimitry Andric void SmartPtrModeling::handleRelease(const CallEvent &Call, 6265ffd83dbSDimitry Andric CheckerContext &C) const { 627e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 6285ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 6295ffd83dbSDimitry Andric if (!IC) 6305ffd83dbSDimitry Andric return; 6315ffd83dbSDimitry Andric 632e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 633e8d8bef9SDimitry Andric if (!ThisRegion) 6345ffd83dbSDimitry Andric return; 6355ffd83dbSDimitry Andric 636e8d8bef9SDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 6375ffd83dbSDimitry Andric 6385ffd83dbSDimitry Andric if (InnerPointVal) { 6395ffd83dbSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 6405ffd83dbSDimitry Andric *InnerPointVal); 6415ffd83dbSDimitry Andric } 642e8d8bef9SDimitry Andric 643e8d8bef9SDimitry Andric auto ValueToUpdate = C.getSValBuilder().makeNull(); 644e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); 645e8d8bef9SDimitry Andric 646e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 647e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 648e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 649e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 650e8d8bef9SDimitry Andric return; 651e8d8bef9SDimitry Andric 652e8d8bef9SDimitry Andric OS << "Smart pointer"; 653e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 654e8d8bef9SDimitry Andric OS << " is released and set to null"; 655e8d8bef9SDimitry Andric })); 6565ffd83dbSDimitry Andric // TODO: Add support to enable MallocChecker to start tracking the raw 6575ffd83dbSDimitry Andric // pointer. 6585ffd83dbSDimitry Andric } 6595ffd83dbSDimitry Andric 660fe6060f1SDimitry Andric void SmartPtrModeling::handleSwapMethod(const CallEvent &Call, 6615ffd83dbSDimitry Andric CheckerContext &C) const { 662e8d8bef9SDimitry Andric // To model unique_ptr::swap() method. 663e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 664e8d8bef9SDimitry Andric if (!IC) 665e8d8bef9SDimitry Andric return; 666e8d8bef9SDimitry Andric 667e8d8bef9SDimitry Andric auto State = C.getState(); 668fe6060f1SDimitry Andric handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C); 669fe6060f1SDimitry Andric } 670e8d8bef9SDimitry Andric 671fe6060f1SDimitry Andric bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First, 672fe6060f1SDimitry Andric SVal Second, CheckerContext &C) const { 673fe6060f1SDimitry Andric const MemRegion *FirstThisRegion = First.getAsRegion(); 674fe6060f1SDimitry Andric if (!FirstThisRegion) 675fe6060f1SDimitry Andric return false; 676fe6060f1SDimitry Andric const MemRegion *SecondThisRegion = Second.getAsRegion(); 677fe6060f1SDimitry Andric if (!SecondThisRegion) 678fe6060f1SDimitry Andric return false; 679e8d8bef9SDimitry Andric 680fe6060f1SDimitry Andric const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion); 681fe6060f1SDimitry Andric const auto *SecondInnerPtrVal = 682fe6060f1SDimitry Andric State->get<TrackedRegionMap>(SecondThisRegion); 683fe6060f1SDimitry Andric 684fe6060f1SDimitry Andric State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal); 685fe6060f1SDimitry Andric State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal); 686fe6060f1SDimitry Andric 687fe6060f1SDimitry Andric C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion]( 688fe6060f1SDimitry Andric PathSensitiveBugReport &BR, 689e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 690fe6060f1SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 691e8d8bef9SDimitry Andric return; 692fe6060f1SDimitry Andric if (BR.isInteresting(FirstThisRegion) && 693fe6060f1SDimitry Andric !BR.isInteresting(SecondThisRegion)) { 694fe6060f1SDimitry Andric BR.markInteresting(SecondThisRegion); 695fe6060f1SDimitry Andric BR.markNotInteresting(FirstThisRegion); 696fe6060f1SDimitry Andric } 697fe6060f1SDimitry Andric if (BR.isInteresting(SecondThisRegion) && 698fe6060f1SDimitry Andric !BR.isInteresting(FirstThisRegion)) { 699fe6060f1SDimitry Andric BR.markInteresting(FirstThisRegion); 700fe6060f1SDimitry Andric BR.markNotInteresting(SecondThisRegion); 701fe6060f1SDimitry Andric } 702fe6060f1SDimitry Andric // TODO: We need to emit some note here probably!! 703e8d8bef9SDimitry Andric })); 704fe6060f1SDimitry Andric 705fe6060f1SDimitry Andric return true; 7065ffd83dbSDimitry Andric } 7075ffd83dbSDimitry Andric 708e8d8bef9SDimitry Andric void SmartPtrModeling::handleGet(const CallEvent &Call, 709e8d8bef9SDimitry Andric CheckerContext &C) const { 7105ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 711e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 712e8d8bef9SDimitry Andric if (!IC) 713e8d8bef9SDimitry Andric return; 7145ffd83dbSDimitry Andric 715e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 716e8d8bef9SDimitry Andric if (!ThisRegion) 717e8d8bef9SDimitry Andric return; 718e8d8bef9SDimitry Andric 719e8d8bef9SDimitry Andric SVal InnerPointerVal; 720fe6060f1SDimitry Andric std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal( 721fe6060f1SDimitry Andric State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C); 722e8d8bef9SDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 723e8d8bef9SDimitry Andric InnerPointerVal); 724e8d8bef9SDimitry Andric // TODO: Add NoteTag, for how the raw pointer got using 'get' method. 725e8d8bef9SDimitry Andric C.addTransition(State); 726e8d8bef9SDimitry Andric } 727e8d8bef9SDimitry Andric 728e8d8bef9SDimitry Andric bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, 729e8d8bef9SDimitry Andric CheckerContext &C) const { 730e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 731e8d8bef9SDimitry Andric const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); 732e8d8bef9SDimitry Andric if (!OC) 733e8d8bef9SDimitry Andric return false; 734e8d8bef9SDimitry Andric OverloadedOperatorKind OOK = OC->getOverloadedOperator(); 735e8d8bef9SDimitry Andric if (OOK != OO_Equal) 736e8d8bef9SDimitry Andric return false; 737e8d8bef9SDimitry Andric const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); 738e8d8bef9SDimitry Andric if (!ThisRegion) 739e8d8bef9SDimitry Andric return false; 740e8d8bef9SDimitry Andric 741e8d8bef9SDimitry Andric const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); 742e8d8bef9SDimitry Andric // In case of 'nullptr' or '0' assigned 743e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion) { 744e8d8bef9SDimitry Andric bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); 745e8d8bef9SDimitry Andric if (!AssignedNull) 746e8d8bef9SDimitry Andric return false; 747e8d8bef9SDimitry Andric auto NullVal = C.getSValBuilder().makeNull(); 748e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 749e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 750e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 751e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 752e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 753e8d8bef9SDimitry Andric return; 754e8d8bef9SDimitry Andric OS << "Smart pointer"; 755e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 756e8d8bef9SDimitry Andric OS << " is assigned to null"; 757e8d8bef9SDimitry Andric })); 758e8d8bef9SDimitry Andric return true; 759e8d8bef9SDimitry Andric } 760e8d8bef9SDimitry Andric 761e8d8bef9SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 762e8d8bef9SDimitry Andric } 763e8d8bef9SDimitry Andric 764e8d8bef9SDimitry Andric bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, 765e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const { 766e8d8bef9SDimitry Andric const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); 767e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion) 768e8d8bef9SDimitry Andric return false; 769e8d8bef9SDimitry Andric 770e8d8bef9SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 771e8d8bef9SDimitry Andric } 772e8d8bef9SDimitry Andric 773e8d8bef9SDimitry Andric bool SmartPtrModeling::updateMovedSmartPointers( 774e8d8bef9SDimitry Andric CheckerContext &C, const MemRegion *ThisRegion, 775e8d8bef9SDimitry Andric const MemRegion *OtherSmartPtrRegion) const { 776e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 777e8d8bef9SDimitry Andric const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); 778e8d8bef9SDimitry Andric if (OtherInnerPtr) { 779e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); 780e8d8bef9SDimitry Andric auto NullVal = C.getSValBuilder().makeNull(); 781e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 782e8d8bef9SDimitry Andric bool IsArgValNull = OtherInnerPtr->isZeroConstant(); 783e8d8bef9SDimitry Andric 784e8d8bef9SDimitry Andric C.addTransition( 785e8d8bef9SDimitry Andric State, 786e8d8bef9SDimitry Andric C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( 787e8d8bef9SDimitry Andric PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 788e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 789e8d8bef9SDimitry Andric return; 790e8d8bef9SDimitry Andric if (BR.isInteresting(OtherSmartPtrRegion)) { 791e8d8bef9SDimitry Andric OS << "Smart pointer"; 792e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 793e8d8bef9SDimitry Andric OS << " is null after being moved to"; 794e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 795e8d8bef9SDimitry Andric } 796e8d8bef9SDimitry Andric if (BR.isInteresting(ThisRegion) && IsArgValNull) { 797e8d8bef9SDimitry Andric OS << "A null pointer value is moved to"; 798e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 799e8d8bef9SDimitry Andric BR.markInteresting(OtherSmartPtrRegion); 800e8d8bef9SDimitry Andric } 801e8d8bef9SDimitry Andric })); 802e8d8bef9SDimitry Andric return true; 803e8d8bef9SDimitry Andric } else { 804e8d8bef9SDimitry Andric // In case we dont know anything about value we are moving from 805e8d8bef9SDimitry Andric // remove the entry from map for which smart pointer got moved to. 806e8d8bef9SDimitry Andric auto NullVal = C.getSValBuilder().makeNull(); 807e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(ThisRegion); 808e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 809e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, 810e8d8bef9SDimitry Andric ThisRegion](PathSensitiveBugReport &BR, 811e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 812e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 813e8d8bef9SDimitry Andric !BR.isInteresting(OtherSmartPtrRegion)) 814e8d8bef9SDimitry Andric return; 815e8d8bef9SDimitry Andric OS << "Smart pointer"; 816e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 817e8d8bef9SDimitry Andric OS << " is null after; previous value moved to"; 818e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 819e8d8bef9SDimitry Andric })); 820e8d8bef9SDimitry Andric return true; 821e8d8bef9SDimitry Andric } 822e8d8bef9SDimitry Andric return false; 823e8d8bef9SDimitry Andric } 824e8d8bef9SDimitry Andric 825e8d8bef9SDimitry Andric void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, 826e8d8bef9SDimitry Andric CheckerContext &C) const { 827e8d8bef9SDimitry Andric // To model unique_ptr::operator bool 828e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 829e8d8bef9SDimitry Andric const Expr *CallExpr = Call.getOriginExpr(); 830e8d8bef9SDimitry Andric const MemRegion *ThisRegion = 831e8d8bef9SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 832e8d8bef9SDimitry Andric 833e8d8bef9SDimitry Andric SVal InnerPointerVal; 834e8d8bef9SDimitry Andric if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { 835e8d8bef9SDimitry Andric InnerPointerVal = *InnerValPtr; 836e8d8bef9SDimitry Andric } else { 837e8d8bef9SDimitry Andric // In case of inner pointer SVal is not available we create 838e8d8bef9SDimitry Andric // conjureSymbolVal for inner pointer value. 839e8d8bef9SDimitry Andric auto InnerPointerType = getInnerPointerType(Call, C); 840e8d8bef9SDimitry Andric if (InnerPointerType.isNull()) 841e8d8bef9SDimitry Andric return; 842e8d8bef9SDimitry Andric 843e8d8bef9SDimitry Andric const LocationContext *LC = C.getLocationContext(); 844e8d8bef9SDimitry Andric InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 845e8d8bef9SDimitry Andric CallExpr, LC, InnerPointerType, C.blockCount()); 846e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 847e8d8bef9SDimitry Andric } 848e8d8bef9SDimitry Andric 849e8d8bef9SDimitry Andric if (State->isNull(InnerPointerVal).isConstrainedTrue()) { 850e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(), 851e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false)); 852e8d8bef9SDimitry Andric 853e8d8bef9SDimitry Andric C.addTransition(State); 854e8d8bef9SDimitry Andric return; 855e8d8bef9SDimitry Andric } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { 856e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(), 857e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true)); 858e8d8bef9SDimitry Andric 859e8d8bef9SDimitry Andric C.addTransition(State); 860e8d8bef9SDimitry Andric return; 861e8d8bef9SDimitry Andric } else if (move::isMovedFrom(State, ThisRegion)) { 862e8d8bef9SDimitry Andric C.addTransition( 863e8d8bef9SDimitry Andric State->BindExpr(CallExpr, C.getLocationContext(), 864e8d8bef9SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType()))); 865e8d8bef9SDimitry Andric return; 866e8d8bef9SDimitry Andric } else { 867e8d8bef9SDimitry Andric ProgramStateRef NotNullState, NullState; 868e8d8bef9SDimitry Andric std::tie(NotNullState, NullState) = 869e8d8bef9SDimitry Andric State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); 870e8d8bef9SDimitry Andric 871e8d8bef9SDimitry Andric auto NullVal = C.getSValBuilder().makeNull(); 872e8d8bef9SDimitry Andric // Explicitly tracking the region as null. 873e8d8bef9SDimitry Andric NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); 874e8d8bef9SDimitry Andric 875e8d8bef9SDimitry Andric NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), 876e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false)); 877e8d8bef9SDimitry Andric C.addTransition(NullState, C.getNoteTag( 878e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR, 879e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 880e8d8bef9SDimitry Andric OS << "Assuming smart pointer"; 881e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 882e8d8bef9SDimitry Andric OS << " is null"; 883e8d8bef9SDimitry Andric }, 884e8d8bef9SDimitry Andric /*IsPrunable=*/true)); 885e8d8bef9SDimitry Andric NotNullState = 886e8d8bef9SDimitry Andric NotNullState->BindExpr(CallExpr, C.getLocationContext(), 887e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true)); 888e8d8bef9SDimitry Andric C.addTransition( 889e8d8bef9SDimitry Andric NotNullState, 890e8d8bef9SDimitry Andric C.getNoteTag( 891e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 892e8d8bef9SDimitry Andric OS << "Assuming smart pointer"; 893e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 894e8d8bef9SDimitry Andric OS << " is non-null"; 895e8d8bef9SDimitry Andric }, 896e8d8bef9SDimitry Andric /*IsPrunable=*/true)); 897e8d8bef9SDimitry Andric return; 898e8d8bef9SDimitry Andric } 8995ffd83dbSDimitry Andric } 9005ffd83dbSDimitry Andric 9015ffd83dbSDimitry Andric void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 9025ffd83dbSDimitry Andric auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 9035ffd83dbSDimitry Andric Checker->ModelSmartPtrDereference = 9045ffd83dbSDimitry Andric Mgr.getAnalyzerOptions().getCheckerBooleanOption( 9055ffd83dbSDimitry Andric Checker, "ModelSmartPtrDereference"); 9065ffd83dbSDimitry Andric } 9075ffd83dbSDimitry Andric 9085ffd83dbSDimitry Andric bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 9095ffd83dbSDimitry Andric const LangOptions &LO = mgr.getLangOpts(); 9100b57cec5SDimitry Andric return LO.CPlusPlus; 9110b57cec5SDimitry Andric } 912