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" 26349cc55cSDimitry 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" 36*bdd1243dSDimitry Andric #include <optional> 37e8d8bef9SDimitry Andric #include <string> 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric using namespace clang; 400b57cec5SDimitry Andric using namespace ento; 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric namespace { 43fe6060f1SDimitry Andric 44e8d8bef9SDimitry Andric class SmartPtrModeling 45e8d8bef9SDimitry Andric : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, 46e8d8bef9SDimitry Andric check::LiveSymbols> { 475ffd83dbSDimitry Andric 48e8d8bef9SDimitry Andric bool isBoolConversionMethod(const CallEvent &Call) const; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric public: 515ffd83dbSDimitry Andric // Whether the checker should model for null dereferences of smart pointers. 5281ad6265SDimitry Andric bool ModelSmartPtrDereference = false; 530b57cec5SDimitry Andric bool evalCall(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, 7481ad6265SDimitry Andric const MemRegion *OtherSmartPtrRegion, 7581ad6265SDimitry Andric const CallEvent &Call) const; 76e8d8bef9SDimitry Andric void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; 77fe6060f1SDimitry Andric bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; 78fe6060f1SDimitry Andric bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; 79fe6060f1SDimitry Andric bool handleSwap(ProgramStateRef State, SVal First, SVal Second, 80fe6060f1SDimitry Andric CheckerContext &C) const; 81fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> 82fe6060f1SDimitry Andric retrieveOrConjureInnerPtrVal(ProgramStateRef State, 83fe6060f1SDimitry Andric const MemRegion *ThisRegion, const Expr *E, 84fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const; 855ffd83dbSDimitry Andric 865ffd83dbSDimitry Andric using SmartPtrMethodHandlerFn = 875ffd83dbSDimitry Andric void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; 885ffd83dbSDimitry Andric CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ 89*bdd1243dSDimitry Andric {{{"reset"}}, &SmartPtrModeling::handleReset}, 90*bdd1243dSDimitry Andric {{{"release"}}, &SmartPtrModeling::handleRelease}, 91*bdd1243dSDimitry Andric {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, 92*bdd1243dSDimitry Andric {{{"get"}}, &SmartPtrModeling::handleGet}}; 93fe6060f1SDimitry Andric const CallDescription StdSwapCall{{"std", "swap"}, 2}; 94fe6060f1SDimitry Andric const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; 95fe6060f1SDimitry Andric const CallDescription StdMakeUniqueForOverwriteCall{ 96fe6060f1SDimitry Andric {"std", "make_unique_for_overwrite"}}; 970b57cec5SDimitry Andric }; 980b57cec5SDimitry Andric } // end of anonymous namespace 990b57cec5SDimitry Andric 1005ffd83dbSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 1015ffd83dbSDimitry Andric 102fe6060f1SDimitry Andric // Checks if RD has name in Names and is in std namespace 103fe6060f1SDimitry Andric static bool hasStdClassWithName(const CXXRecordDecl *RD, 104fe6060f1SDimitry Andric ArrayRef<llvm::StringLiteral> Names) { 105fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 106fe6060f1SDimitry Andric return false; 107349cc55cSDimitry Andric if (RD->getDeclName().isIdentifier()) 108349cc55cSDimitry Andric return llvm::is_contained(Names, RD->getName()); 109fe6060f1SDimitry Andric return false; 110fe6060f1SDimitry Andric } 111fe6060f1SDimitry Andric 112fe6060f1SDimitry Andric constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr", 113fe6060f1SDimitry Andric "weak_ptr"}; 114fe6060f1SDimitry Andric 115fe6060f1SDimitry Andric static bool isStdSmartPtr(const CXXRecordDecl *RD) { 116fe6060f1SDimitry Andric return hasStdClassWithName(RD, STD_PTR_NAMES); 117fe6060f1SDimitry Andric } 118fe6060f1SDimitry Andric 119fe6060f1SDimitry Andric static bool isStdSmartPtr(const Expr *E) { 120fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 121fe6060f1SDimitry Andric } 122fe6060f1SDimitry Andric 1235ffd83dbSDimitry Andric // Define the inter-checker API. 1245ffd83dbSDimitry Andric namespace clang { 1255ffd83dbSDimitry Andric namespace ento { 1265ffd83dbSDimitry Andric namespace smartptr { 1275ffd83dbSDimitry Andric bool isStdSmartPtrCall(const CallEvent &Call) { 1285ffd83dbSDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 1295ffd83dbSDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 1305ffd83dbSDimitry Andric return false; 131fe6060f1SDimitry Andric return isStdSmartPtr(MethodDecl->getParent()); 132fe6060f1SDimitry Andric } 1335ffd83dbSDimitry Andric 134fe6060f1SDimitry Andric bool isStdSmartPtr(const CXXRecordDecl *RD) { 135fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 1365ffd83dbSDimitry Andric return false; 1375ffd83dbSDimitry Andric 138fe6060f1SDimitry Andric if (RD->getDeclName().isIdentifier()) { 139fe6060f1SDimitry Andric StringRef Name = RD->getName(); 1405ffd83dbSDimitry Andric return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 1415ffd83dbSDimitry Andric } 1425ffd83dbSDimitry Andric return false; 1435ffd83dbSDimitry Andric } 1445ffd83dbSDimitry Andric 145fe6060f1SDimitry Andric bool isStdSmartPtr(const Expr *E) { 146fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 147fe6060f1SDimitry Andric } 148fe6060f1SDimitry Andric 1495ffd83dbSDimitry Andric bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 1505ffd83dbSDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 151e8d8bef9SDimitry Andric return InnerPointVal && 152e8d8bef9SDimitry Andric !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); 1535ffd83dbSDimitry Andric } 1545ffd83dbSDimitry Andric } // namespace smartptr 1555ffd83dbSDimitry Andric } // namespace ento 1565ffd83dbSDimitry Andric } // namespace clang 1575ffd83dbSDimitry Andric 158e8d8bef9SDimitry Andric // If a region is removed all of the subregions need to be removed too. 159e8d8bef9SDimitry Andric static TrackedRegionMapTy 160e8d8bef9SDimitry Andric removeTrackedSubregions(TrackedRegionMapTy RegionMap, 161e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory, 162e8d8bef9SDimitry Andric const MemRegion *Region) { 163e8d8bef9SDimitry Andric if (!Region) 164e8d8bef9SDimitry Andric return RegionMap; 165e8d8bef9SDimitry Andric for (const auto &E : RegionMap) { 166e8d8bef9SDimitry Andric if (E.first->isSubRegionOf(Region)) 167e8d8bef9SDimitry Andric RegionMap = RegionMapFactory.remove(RegionMap, E.first); 168e8d8bef9SDimitry Andric } 169e8d8bef9SDimitry Andric return RegionMap; 170e8d8bef9SDimitry Andric } 171e8d8bef9SDimitry Andric 172e8d8bef9SDimitry Andric static ProgramStateRef updateSwappedRegion(ProgramStateRef State, 173e8d8bef9SDimitry Andric const MemRegion *Region, 174e8d8bef9SDimitry Andric const SVal *RegionInnerPointerVal) { 175e8d8bef9SDimitry Andric if (RegionInnerPointerVal) { 176e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); 177e8d8bef9SDimitry Andric } else { 178e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(Region); 179e8d8bef9SDimitry Andric } 180e8d8bef9SDimitry Andric return State; 181e8d8bef9SDimitry Andric } 182e8d8bef9SDimitry Andric 183fe6060f1SDimitry Andric static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { 184fe6060f1SDimitry Andric if (!RD || !RD->isInStdNamespace()) 185fe6060f1SDimitry Andric return {}; 186fe6060f1SDimitry Andric 187fe6060f1SDimitry Andric const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); 188fe6060f1SDimitry Andric if (!TSD) 189fe6060f1SDimitry Andric return {}; 190fe6060f1SDimitry Andric 191fe6060f1SDimitry Andric auto TemplateArgs = TSD->getTemplateArgs().asArray(); 192fe6060f1SDimitry Andric if (TemplateArgs.empty()) 193fe6060f1SDimitry Andric return {}; 194fe6060f1SDimitry Andric auto InnerValueType = TemplateArgs[0].getAsType(); 195fe6060f1SDimitry Andric return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); 196fe6060f1SDimitry Andric } 197fe6060f1SDimitry Andric 198fe6060f1SDimitry Andric // This is for use with standalone-functions like std::make_unique, 199fe6060f1SDimitry Andric // std::make_unique_for_overwrite, etc. It reads the template parameter and 200fe6060f1SDimitry Andric // returns the pointer type corresponding to it, 201fe6060f1SDimitry Andric static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, 202fe6060f1SDimitry Andric CheckerContext &C) { 203fe6060f1SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 204fe6060f1SDimitry Andric if (!FD || !FD->isFunctionTemplateSpecialization()) 205fe6060f1SDimitry Andric return {}; 206fe6060f1SDimitry Andric const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); 207fe6060f1SDimitry Andric if (TemplateArgs.size() == 0) 208fe6060f1SDimitry Andric return {}; 209fe6060f1SDimitry Andric auto ValueType = TemplateArgs[0].getAsType(); 210fe6060f1SDimitry Andric return C.getASTContext().getPointerType(ValueType.getCanonicalType()); 211fe6060f1SDimitry Andric } 212fe6060f1SDimitry Andric 213e8d8bef9SDimitry Andric // Helper method to get the inner pointer type of specialized smart pointer 214e8d8bef9SDimitry Andric // Returns empty type if not found valid inner pointer type. 215e8d8bef9SDimitry Andric static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { 216e8d8bef9SDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 217e8d8bef9SDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 218e8d8bef9SDimitry Andric return {}; 219e8d8bef9SDimitry Andric 220e8d8bef9SDimitry Andric const auto *RecordDecl = MethodDecl->getParent(); 221fe6060f1SDimitry Andric return getInnerPointerType(C, RecordDecl); 222e8d8bef9SDimitry Andric } 223e8d8bef9SDimitry Andric 224e8d8bef9SDimitry Andric // Helper method to pretty print region and avoid extra spacing. 225e8d8bef9SDimitry Andric static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, 226e8d8bef9SDimitry Andric const MemRegion *Region) { 227e8d8bef9SDimitry Andric if (Region->canPrintPretty()) { 228e8d8bef9SDimitry Andric OS << " "; 229e8d8bef9SDimitry Andric Region->printPretty(OS); 230e8d8bef9SDimitry Andric } 231e8d8bef9SDimitry Andric } 232e8d8bef9SDimitry Andric 233e8d8bef9SDimitry Andric bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { 2340b57cec5SDimitry Andric // TODO: Update CallDescription to support anonymous calls? 2350b57cec5SDimitry Andric // TODO: Handle other methods, such as .get() or .release(). 2360b57cec5SDimitry Andric // But once we do, we'd need a visitor to explain null dereferences 2370b57cec5SDimitry Andric // that are found via such modeling. 2380b57cec5SDimitry Andric const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 2390b57cec5SDimitry Andric return CD && CD->getConversionType()->isBooleanType(); 2400b57cec5SDimitry Andric } 2410b57cec5SDimitry Andric 242fe6060f1SDimitry Andric constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"}; 243fe6060f1SDimitry Andric 244fe6060f1SDimitry Andric bool isStdBasicOstream(const Expr *E) { 245fe6060f1SDimitry Andric const auto *RD = E->getType()->getAsCXXRecordDecl(); 246fe6060f1SDimitry Andric return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES); 247fe6060f1SDimitry Andric } 248fe6060f1SDimitry Andric 249fe6060f1SDimitry Andric static bool isStdFunctionCall(const CallEvent &Call) { 250fe6060f1SDimitry Andric return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace(); 251fe6060f1SDimitry Andric } 252fe6060f1SDimitry Andric 253fe6060f1SDimitry Andric bool isStdOstreamOperatorCall(const CallEvent &Call) { 254fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 255fe6060f1SDimitry Andric return false; 256fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 257fe6060f1SDimitry Andric if (!FC) 258fe6060f1SDimitry Andric return false; 259fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 260fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 261fe6060f1SDimitry Andric return false; 262fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 263fe6060f1SDimitry Andric if (OOK != clang::OO_LessLess) 264fe6060f1SDimitry Andric return false; 265fe6060f1SDimitry Andric return isStdSmartPtr(Call.getArgExpr(1)) && 266fe6060f1SDimitry Andric isStdBasicOstream(Call.getArgExpr(0)); 267fe6060f1SDimitry Andric } 268fe6060f1SDimitry Andric 269fe6060f1SDimitry Andric static bool isPotentiallyComparisionOpCall(const CallEvent &Call) { 270fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 271fe6060f1SDimitry Andric return false; 272fe6060f1SDimitry Andric return smartptr::isStdSmartPtr(Call.getArgExpr(0)) || 273fe6060f1SDimitry Andric smartptr::isStdSmartPtr(Call.getArgExpr(1)); 274fe6060f1SDimitry Andric } 275fe6060f1SDimitry Andric 2760b57cec5SDimitry Andric bool SmartPtrModeling::evalCall(const CallEvent &Call, 2770b57cec5SDimitry Andric CheckerContext &C) const { 278fe6060f1SDimitry Andric 279e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 280fe6060f1SDimitry Andric 281fe6060f1SDimitry Andric // If any one of the arg is a unique_ptr, then 282fe6060f1SDimitry Andric // we can try this function 283fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call)) 284fe6060f1SDimitry Andric if (handleComparisionOp(Call, C)) 285fe6060f1SDimitry Andric return true; 286fe6060f1SDimitry Andric 287fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) 288fe6060f1SDimitry Andric return handleOstreamOperator(Call, C); 289fe6060f1SDimitry Andric 290349cc55cSDimitry Andric if (StdSwapCall.matches(Call)) { 291fe6060f1SDimitry Andric // Check the first arg, if it is of std::unique_ptr type. 292fe6060f1SDimitry Andric assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); 293fe6060f1SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0); 294fe6060f1SDimitry Andric if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) 295fe6060f1SDimitry Andric return false; 296fe6060f1SDimitry Andric return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); 297fe6060f1SDimitry Andric } 298fe6060f1SDimitry Andric 299349cc55cSDimitry Andric if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { 300fe6060f1SDimitry Andric if (!ModelSmartPtrDereference) 301fe6060f1SDimitry Andric return false; 302fe6060f1SDimitry Andric 303*bdd1243dSDimitry Andric const std::optional<SVal> ThisRegionOpt = 304*bdd1243dSDimitry Andric Call.getReturnValueUnderConstruction(); 305fe6060f1SDimitry Andric if (!ThisRegionOpt) 306fe6060f1SDimitry Andric return false; 307fe6060f1SDimitry Andric 308fe6060f1SDimitry Andric const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( 309fe6060f1SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 310fe6060f1SDimitry Andric getPointerTypeFromTemplateArg(Call, C), C.blockCount()); 311fe6060f1SDimitry Andric 312fe6060f1SDimitry Andric const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); 313fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); 314fe6060f1SDimitry Andric State = State->assume(PtrVal, true); 315fe6060f1SDimitry Andric 316fe6060f1SDimitry Andric // TODO: ExprEngine should do this for us. 317fe6060f1SDimitry Andric // For a bit more context: 318fe6060f1SDimitry Andric // 1) Why do we need this? Since we are modelling a "function" 319fe6060f1SDimitry Andric // that returns a constructed object we need to store this information in 320fe6060f1SDimitry Andric // the program state. 321fe6060f1SDimitry Andric // 322fe6060f1SDimitry Andric // 2) Why does this work? 323fe6060f1SDimitry Andric // `updateObjectsUnderConstruction` does exactly as it sounds. 324fe6060f1SDimitry Andric // 325fe6060f1SDimitry Andric // 3) How should it look like when moved to the Engine? 326fe6060f1SDimitry Andric // It would be nice if we can just 327fe6060f1SDimitry Andric // pretend we don't need to know about this - ie, completely automatic work. 328fe6060f1SDimitry Andric // However, realistically speaking, I think we would need to "signal" the 329fe6060f1SDimitry Andric // ExprEngine evalCall handler that we are constructing an object with this 330fe6060f1SDimitry Andric // function call (constructors obviously construct, hence can be 331fe6060f1SDimitry Andric // automatically deduced). 332fe6060f1SDimitry Andric auto &Engine = State->getStateManager().getOwningEngine(); 333fe6060f1SDimitry Andric State = Engine.updateObjectsUnderConstruction( 334fe6060f1SDimitry Andric *ThisRegionOpt, nullptr, State, C.getLocationContext(), 335fe6060f1SDimitry Andric Call.getConstructionContext(), {}); 336fe6060f1SDimitry Andric 337fe6060f1SDimitry Andric // We don't leave a note here since it is guaranteed the 338fe6060f1SDimitry Andric // unique_ptr from this call is non-null (hence is safe to de-reference). 339fe6060f1SDimitry Andric C.addTransition(State); 340fe6060f1SDimitry Andric return true; 341fe6060f1SDimitry Andric } 342fe6060f1SDimitry Andric 3435ffd83dbSDimitry Andric if (!smartptr::isStdSmartPtrCall(Call)) 3440b57cec5SDimitry Andric return false; 3450b57cec5SDimitry Andric 346e8d8bef9SDimitry Andric if (isBoolConversionMethod(Call)) { 3470b57cec5SDimitry Andric const MemRegion *ThisR = 3480b57cec5SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 3490b57cec5SDimitry Andric 350e8d8bef9SDimitry Andric if (ModelSmartPtrDereference) { 351e8d8bef9SDimitry Andric // The check for the region is moved is duplicated in handleBoolOperation 352e8d8bef9SDimitry Andric // method. 353e8d8bef9SDimitry Andric // FIXME: Once we model std::move for smart pointers clean up this and use 354e8d8bef9SDimitry Andric // that modeling. 355e8d8bef9SDimitry Andric handleBoolConversion(Call, C); 356e8d8bef9SDimitry Andric return true; 357e8d8bef9SDimitry Andric } else { 3580b57cec5SDimitry Andric if (!move::isMovedFrom(State, ThisR)) { 359e8d8bef9SDimitry Andric // TODO: Model this case as well. At least, avoid invalidation of 360e8d8bef9SDimitry Andric // globals. 3610b57cec5SDimitry Andric return false; 3620b57cec5SDimitry Andric } 3630b57cec5SDimitry Andric 3640b57cec5SDimitry Andric // TODO: Add a note to bug reports describing this decision. 365e8d8bef9SDimitry Andric C.addTransition(State->BindExpr( 366e8d8bef9SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 3670b57cec5SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType()))); 368e8d8bef9SDimitry Andric 3690b57cec5SDimitry Andric return true; 3700b57cec5SDimitry Andric } 371e8d8bef9SDimitry Andric } 3720b57cec5SDimitry Andric 3735ffd83dbSDimitry Andric if (!ModelSmartPtrDereference) 3745ffd83dbSDimitry Andric return false; 3755ffd83dbSDimitry Andric 3765ffd83dbSDimitry Andric if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 377e8d8bef9SDimitry Andric if (CC->getDecl()->isCopyConstructor()) 3785ffd83dbSDimitry Andric return false; 3795ffd83dbSDimitry Andric 380e8d8bef9SDimitry Andric const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 381e8d8bef9SDimitry Andric if (!ThisRegion) 3825ffd83dbSDimitry Andric return false; 3835ffd83dbSDimitry Andric 38481ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 38581ad6265SDimitry Andric 386e8d8bef9SDimitry Andric if (CC->getDecl()->isMoveConstructor()) 387e8d8bef9SDimitry Andric return handleMoveCtr(Call, C, ThisRegion); 388e8d8bef9SDimitry Andric 389e8d8bef9SDimitry Andric if (Call.getNumArgs() == 0) { 39081ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 391e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 392e8d8bef9SDimitry Andric 393e8d8bef9SDimitry Andric C.addTransition( 394e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 395e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 396e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 397e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 398e8d8bef9SDimitry Andric return; 399e8d8bef9SDimitry Andric OS << "Default constructed smart pointer"; 400e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 401e8d8bef9SDimitry Andric OS << " is null"; 402e8d8bef9SDimitry Andric })); 403e8d8bef9SDimitry Andric } else { 404e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0); 405e8d8bef9SDimitry Andric assert(TrackingExpr->getType()->isPointerType() && 406e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap"); 407e8d8bef9SDimitry Andric auto ArgVal = Call.getArgSVal(0); 408e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 409e8d8bef9SDimitry Andric 410e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 411e8d8bef9SDimitry Andric ArgVal](PathSensitiveBugReport &BR, 412e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 413e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 414e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 415e8d8bef9SDimitry Andric return; 416e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 417e8d8bef9SDimitry Andric OS << "Smart pointer"; 418e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 419e8d8bef9SDimitry Andric if (ArgVal.isZeroConstant()) 420e8d8bef9SDimitry Andric OS << " is constructed using a null value"; 421e8d8bef9SDimitry Andric else 422e8d8bef9SDimitry Andric OS << " is constructed"; 423e8d8bef9SDimitry Andric })); 424e8d8bef9SDimitry Andric } 4255ffd83dbSDimitry Andric return true; 4260b57cec5SDimitry Andric } 4270b57cec5SDimitry Andric 428e8d8bef9SDimitry Andric if (handleAssignOp(Call, C)) 429e8d8bef9SDimitry Andric return true; 430e8d8bef9SDimitry Andric 4315ffd83dbSDimitry Andric const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 4325ffd83dbSDimitry Andric if (!Handler) 4335ffd83dbSDimitry Andric return false; 4345ffd83dbSDimitry Andric (this->**Handler)(Call, C); 4355ffd83dbSDimitry Andric 4365ffd83dbSDimitry Andric return C.isDifferent(); 4375ffd83dbSDimitry Andric } 4385ffd83dbSDimitry Andric 439fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( 440fe6060f1SDimitry Andric ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E, 441fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const { 442fe6060f1SDimitry Andric const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); 443fe6060f1SDimitry Andric if (Ptr) 444fe6060f1SDimitry Andric return {*Ptr, State}; 445fe6060f1SDimitry Andric auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(), 446fe6060f1SDimitry Andric Type, C.blockCount()); 447fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Val); 448fe6060f1SDimitry Andric return {Val, State}; 449fe6060f1SDimitry Andric } 450fe6060f1SDimitry Andric 451fe6060f1SDimitry Andric bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, 452fe6060f1SDimitry Andric CheckerContext &C) const { 453fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 454fe6060f1SDimitry Andric if (!FC) 455fe6060f1SDimitry Andric return false; 456fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 457fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 458fe6060f1SDimitry Andric return false; 459fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 460fe6060f1SDimitry Andric if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || 461fe6060f1SDimitry Andric OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || 462fe6060f1SDimitry Andric OOK == OO_Spaceship)) 463fe6060f1SDimitry Andric return false; 464fe6060f1SDimitry Andric 465fe6060f1SDimitry Andric // There are some special cases about which we can infer about 466fe6060f1SDimitry Andric // the resulting answer. 467fe6060f1SDimitry Andric // For reference, there is a discussion at https://reviews.llvm.org/D104616. 468fe6060f1SDimitry Andric // Also, the cppreference page is good to look at 469fe6060f1SDimitry Andric // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. 470fe6060f1SDimitry Andric 471fe6060f1SDimitry Andric auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, 472fe6060f1SDimitry Andric SVal S) -> std::pair<SVal, ProgramStateRef> { 473fe6060f1SDimitry Andric if (S.isZeroConstant()) { 474fe6060f1SDimitry Andric return {S, State}; 475fe6060f1SDimitry Andric } 476fe6060f1SDimitry Andric const MemRegion *Reg = S.getAsRegion(); 477fe6060f1SDimitry Andric assert(Reg && 478fe6060f1SDimitry Andric "this pointer of std::unique_ptr should be obtainable as MemRegion"); 479fe6060f1SDimitry Andric QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); 480fe6060f1SDimitry Andric return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C); 481fe6060f1SDimitry Andric }; 482fe6060f1SDimitry Andric 483fe6060f1SDimitry Andric SVal First = Call.getArgSVal(0); 484fe6060f1SDimitry Andric SVal Second = Call.getArgSVal(1); 485fe6060f1SDimitry Andric const auto *FirstExpr = Call.getArgExpr(0); 486fe6060f1SDimitry Andric const auto *SecondExpr = Call.getArgExpr(1); 487fe6060f1SDimitry Andric 488fe6060f1SDimitry Andric const auto *ResultExpr = Call.getOriginExpr(); 489fe6060f1SDimitry Andric const auto *LCtx = C.getLocationContext(); 490fe6060f1SDimitry Andric auto &Bldr = C.getSValBuilder(); 491fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 492fe6060f1SDimitry Andric 493fe6060f1SDimitry Andric SVal FirstPtrVal, SecondPtrVal; 494fe6060f1SDimitry Andric std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First); 495fe6060f1SDimitry Andric std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second); 496fe6060f1SDimitry Andric BinaryOperatorKind BOK = 497fe6060f1SDimitry Andric operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe(); 498fe6060f1SDimitry Andric auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal, 499fe6060f1SDimitry Andric Call.getResultType()); 500fe6060f1SDimitry Andric 501fe6060f1SDimitry Andric if (OOK != OO_Spaceship) { 502fe6060f1SDimitry Andric ProgramStateRef TrueState, FalseState; 503fe6060f1SDimitry Andric std::tie(TrueState, FalseState) = 504fe6060f1SDimitry Andric State->assume(*RetVal.getAs<DefinedOrUnknownSVal>()); 505fe6060f1SDimitry Andric if (TrueState) 506fe6060f1SDimitry Andric C.addTransition( 507fe6060f1SDimitry Andric TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true))); 508fe6060f1SDimitry Andric if (FalseState) 509fe6060f1SDimitry Andric C.addTransition( 510fe6060f1SDimitry Andric FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false))); 511fe6060f1SDimitry Andric } else { 512fe6060f1SDimitry Andric C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal)); 513fe6060f1SDimitry Andric } 514fe6060f1SDimitry Andric return true; 515fe6060f1SDimitry Andric } 516fe6060f1SDimitry Andric 517fe6060f1SDimitry Andric bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call, 518fe6060f1SDimitry Andric CheckerContext &C) const { 519fe6060f1SDimitry Andric // operator<< does not modify the smart pointer. 520fe6060f1SDimitry Andric // And we don't really have much of modelling of basic_ostream. 521fe6060f1SDimitry Andric // So, we are better off: 522fe6060f1SDimitry Andric // 1) Invalidating the mem-region of the ostream object at hand. 523fe6060f1SDimitry Andric // 2) Setting the SVal of the basic_ostream as the return value. 524fe6060f1SDimitry Andric // Not very satisfying, but it gets the job done, and is better 525fe6060f1SDimitry Andric // than the default handling. :) 526fe6060f1SDimitry Andric 527fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 528fe6060f1SDimitry Andric const auto StreamVal = Call.getArgSVal(0); 529fe6060f1SDimitry Andric const MemRegion *StreamThisRegion = StreamVal.getAsRegion(); 530fe6060f1SDimitry Andric if (!StreamThisRegion) 531fe6060f1SDimitry Andric return false; 532fe6060f1SDimitry Andric State = 533fe6060f1SDimitry Andric State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(), 534fe6060f1SDimitry Andric C.blockCount(), C.getLocationContext(), false); 535fe6060f1SDimitry Andric State = 536fe6060f1SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal); 537fe6060f1SDimitry Andric C.addTransition(State); 538fe6060f1SDimitry Andric return true; 539fe6060f1SDimitry Andric } 540fe6060f1SDimitry Andric 5415ffd83dbSDimitry Andric void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 5425ffd83dbSDimitry Andric CheckerContext &C) const { 5435ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 5445ffd83dbSDimitry Andric // Clean up dead regions from the region map. 5455ffd83dbSDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 5465ffd83dbSDimitry Andric for (auto E : TrackedRegions) { 5475ffd83dbSDimitry Andric const MemRegion *Region = E.first; 5485ffd83dbSDimitry Andric bool IsRegDead = !SymReaper.isLiveRegion(Region); 5495ffd83dbSDimitry Andric 5505ffd83dbSDimitry Andric if (IsRegDead) 5515ffd83dbSDimitry Andric State = State->remove<TrackedRegionMap>(Region); 5525ffd83dbSDimitry Andric } 5535ffd83dbSDimitry Andric C.addTransition(State); 5545ffd83dbSDimitry Andric } 5555ffd83dbSDimitry Andric 556e8d8bef9SDimitry Andric void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, 557e8d8bef9SDimitry Andric const char *NL, const char *Sep) const { 558e8d8bef9SDimitry Andric TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 559e8d8bef9SDimitry Andric 560e8d8bef9SDimitry Andric if (!RS.isEmpty()) { 561e8d8bef9SDimitry Andric Out << Sep << "Smart ptr regions :" << NL; 562e8d8bef9SDimitry Andric for (auto I : RS) { 563e8d8bef9SDimitry Andric I.first->dumpToStream(Out); 564e8d8bef9SDimitry Andric if (smartptr::isNullSmartPtr(State, I.first)) 565e8d8bef9SDimitry Andric Out << ": Null"; 566e8d8bef9SDimitry Andric else 567e8d8bef9SDimitry Andric Out << ": Non Null"; 568e8d8bef9SDimitry Andric Out << NL; 569e8d8bef9SDimitry Andric } 570e8d8bef9SDimitry Andric } 571e8d8bef9SDimitry Andric } 572e8d8bef9SDimitry Andric 573e8d8bef9SDimitry Andric ProgramStateRef SmartPtrModeling::checkRegionChanges( 574e8d8bef9SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated, 575e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 576e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 577e8d8bef9SDimitry Andric const CallEvent *Call) const { 578e8d8bef9SDimitry Andric TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); 579e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory = 580e8d8bef9SDimitry Andric State->get_context<TrackedRegionMap>(); 581e8d8bef9SDimitry Andric for (const auto *Region : Regions) 582e8d8bef9SDimitry Andric RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, 583e8d8bef9SDimitry Andric Region->getBaseRegion()); 584e8d8bef9SDimitry Andric return State->set<TrackedRegionMap>(RegionMap); 585e8d8bef9SDimitry Andric } 586e8d8bef9SDimitry Andric 587e8d8bef9SDimitry Andric void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, 588e8d8bef9SDimitry Andric SymbolReaper &SR) const { 589e8d8bef9SDimitry Andric // Marking tracked symbols alive 590e8d8bef9SDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 591e8d8bef9SDimitry Andric for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { 592e8d8bef9SDimitry Andric SVal Val = I->second; 593e8d8bef9SDimitry Andric for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { 594e8d8bef9SDimitry Andric SR.markLive(*si); 595e8d8bef9SDimitry Andric } 596e8d8bef9SDimitry Andric } 597e8d8bef9SDimitry Andric } 598e8d8bef9SDimitry Andric 5995ffd83dbSDimitry Andric void SmartPtrModeling::handleReset(const CallEvent &Call, 6005ffd83dbSDimitry Andric CheckerContext &C) const { 601e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 6025ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 6035ffd83dbSDimitry Andric if (!IC) 6045ffd83dbSDimitry Andric return; 6055ffd83dbSDimitry Andric 606e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 607e8d8bef9SDimitry Andric if (!ThisRegion) 6085ffd83dbSDimitry Andric return; 609e8d8bef9SDimitry Andric 610e8d8bef9SDimitry Andric assert(Call.getArgExpr(0)->getType()->isPointerType() && 611e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap"); 612e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); 613e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0); 614e8d8bef9SDimitry Andric C.addTransition( 615e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, 616e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 617e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 618e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 619e8d8bef9SDimitry Andric return; 620e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 621e8d8bef9SDimitry Andric OS << "Smart pointer"; 622e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 623e8d8bef9SDimitry Andric OS << " reset using a null value"; 624e8d8bef9SDimitry Andric })); 625e8d8bef9SDimitry Andric // TODO: Make sure to ivalidate the region in the Store if we don't have 6265ffd83dbSDimitry Andric // time to model all methods. 6275ffd83dbSDimitry Andric } 6285ffd83dbSDimitry Andric 6295ffd83dbSDimitry Andric void SmartPtrModeling::handleRelease(const CallEvent &Call, 6305ffd83dbSDimitry Andric CheckerContext &C) const { 631e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 6325ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 6335ffd83dbSDimitry Andric if (!IC) 6345ffd83dbSDimitry Andric return; 6355ffd83dbSDimitry Andric 636e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 637e8d8bef9SDimitry Andric if (!ThisRegion) 6385ffd83dbSDimitry Andric return; 6395ffd83dbSDimitry Andric 640e8d8bef9SDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 6415ffd83dbSDimitry Andric 6425ffd83dbSDimitry Andric if (InnerPointVal) { 6435ffd83dbSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 6445ffd83dbSDimitry Andric *InnerPointVal); 6455ffd83dbSDimitry Andric } 646e8d8bef9SDimitry Andric 64781ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 64881ad6265SDimitry Andric auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType); 649e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); 650e8d8bef9SDimitry Andric 651e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 652e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 653e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 654e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 655e8d8bef9SDimitry Andric return; 656e8d8bef9SDimitry Andric 657e8d8bef9SDimitry Andric OS << "Smart pointer"; 658e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 659e8d8bef9SDimitry Andric OS << " is released and set to null"; 660e8d8bef9SDimitry Andric })); 6615ffd83dbSDimitry Andric // TODO: Add support to enable MallocChecker to start tracking the raw 6625ffd83dbSDimitry Andric // pointer. 6635ffd83dbSDimitry Andric } 6645ffd83dbSDimitry Andric 665fe6060f1SDimitry Andric void SmartPtrModeling::handleSwapMethod(const CallEvent &Call, 6665ffd83dbSDimitry Andric CheckerContext &C) const { 667e8d8bef9SDimitry Andric // To model unique_ptr::swap() method. 668e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 669e8d8bef9SDimitry Andric if (!IC) 670e8d8bef9SDimitry Andric return; 671e8d8bef9SDimitry Andric 672e8d8bef9SDimitry Andric auto State = C.getState(); 673fe6060f1SDimitry Andric handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C); 674fe6060f1SDimitry Andric } 675e8d8bef9SDimitry Andric 676fe6060f1SDimitry Andric bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First, 677fe6060f1SDimitry Andric SVal Second, CheckerContext &C) const { 678fe6060f1SDimitry Andric const MemRegion *FirstThisRegion = First.getAsRegion(); 679fe6060f1SDimitry Andric if (!FirstThisRegion) 680fe6060f1SDimitry Andric return false; 681fe6060f1SDimitry Andric const MemRegion *SecondThisRegion = Second.getAsRegion(); 682fe6060f1SDimitry Andric if (!SecondThisRegion) 683fe6060f1SDimitry Andric return false; 684e8d8bef9SDimitry Andric 685fe6060f1SDimitry Andric const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion); 686fe6060f1SDimitry Andric const auto *SecondInnerPtrVal = 687fe6060f1SDimitry Andric State->get<TrackedRegionMap>(SecondThisRegion); 688fe6060f1SDimitry Andric 689fe6060f1SDimitry Andric State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal); 690fe6060f1SDimitry Andric State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal); 691fe6060f1SDimitry Andric 692fe6060f1SDimitry Andric C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion]( 693fe6060f1SDimitry Andric PathSensitiveBugReport &BR, 694e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 695fe6060f1SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 696e8d8bef9SDimitry Andric return; 697fe6060f1SDimitry Andric if (BR.isInteresting(FirstThisRegion) && 698fe6060f1SDimitry Andric !BR.isInteresting(SecondThisRegion)) { 699fe6060f1SDimitry Andric BR.markInteresting(SecondThisRegion); 700fe6060f1SDimitry Andric BR.markNotInteresting(FirstThisRegion); 701fe6060f1SDimitry Andric } 702fe6060f1SDimitry Andric if (BR.isInteresting(SecondThisRegion) && 703fe6060f1SDimitry Andric !BR.isInteresting(FirstThisRegion)) { 704fe6060f1SDimitry Andric BR.markInteresting(FirstThisRegion); 705fe6060f1SDimitry Andric BR.markNotInteresting(SecondThisRegion); 706fe6060f1SDimitry Andric } 707fe6060f1SDimitry Andric // TODO: We need to emit some note here probably!! 708e8d8bef9SDimitry Andric })); 709fe6060f1SDimitry Andric 710fe6060f1SDimitry Andric return true; 7115ffd83dbSDimitry Andric } 7125ffd83dbSDimitry Andric 713e8d8bef9SDimitry Andric void SmartPtrModeling::handleGet(const CallEvent &Call, 714e8d8bef9SDimitry Andric CheckerContext &C) const { 7155ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 716e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 717e8d8bef9SDimitry Andric if (!IC) 718e8d8bef9SDimitry Andric return; 7195ffd83dbSDimitry Andric 720e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 721e8d8bef9SDimitry Andric if (!ThisRegion) 722e8d8bef9SDimitry Andric return; 723e8d8bef9SDimitry Andric 724e8d8bef9SDimitry Andric SVal InnerPointerVal; 725fe6060f1SDimitry Andric std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal( 726fe6060f1SDimitry Andric State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C); 727e8d8bef9SDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 728e8d8bef9SDimitry Andric InnerPointerVal); 729e8d8bef9SDimitry Andric // TODO: Add NoteTag, for how the raw pointer got using 'get' method. 730e8d8bef9SDimitry Andric C.addTransition(State); 731e8d8bef9SDimitry Andric } 732e8d8bef9SDimitry Andric 733e8d8bef9SDimitry Andric bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, 734e8d8bef9SDimitry Andric CheckerContext &C) const { 735e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 736e8d8bef9SDimitry Andric const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); 737e8d8bef9SDimitry Andric if (!OC) 738e8d8bef9SDimitry Andric return false; 739e8d8bef9SDimitry Andric OverloadedOperatorKind OOK = OC->getOverloadedOperator(); 740e8d8bef9SDimitry Andric if (OOK != OO_Equal) 741e8d8bef9SDimitry Andric return false; 742e8d8bef9SDimitry Andric const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); 743e8d8bef9SDimitry Andric if (!ThisRegion) 744e8d8bef9SDimitry Andric return false; 745e8d8bef9SDimitry Andric 74681ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 74781ad6265SDimitry Andric 748e8d8bef9SDimitry Andric const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); 749e8d8bef9SDimitry Andric // In case of 'nullptr' or '0' assigned 750e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion) { 751e8d8bef9SDimitry Andric bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); 752e8d8bef9SDimitry Andric if (!AssignedNull) 753e8d8bef9SDimitry Andric return false; 75481ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 755e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 756e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 757e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 758e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 759e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 760e8d8bef9SDimitry Andric return; 761e8d8bef9SDimitry Andric OS << "Smart pointer"; 762e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 763e8d8bef9SDimitry Andric OS << " is assigned to null"; 764e8d8bef9SDimitry Andric })); 765e8d8bef9SDimitry Andric return true; 766e8d8bef9SDimitry Andric } 767e8d8bef9SDimitry Andric 76881ad6265SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); 769e8d8bef9SDimitry Andric } 770e8d8bef9SDimitry Andric 771e8d8bef9SDimitry Andric bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, 772e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const { 773e8d8bef9SDimitry Andric const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); 774e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion) 775e8d8bef9SDimitry Andric return false; 776e8d8bef9SDimitry Andric 77781ad6265SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); 778e8d8bef9SDimitry Andric } 779e8d8bef9SDimitry Andric 780e8d8bef9SDimitry Andric bool SmartPtrModeling::updateMovedSmartPointers( 781e8d8bef9SDimitry Andric CheckerContext &C, const MemRegion *ThisRegion, 78281ad6265SDimitry Andric const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const { 783e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 78481ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 785e8d8bef9SDimitry Andric const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); 786e8d8bef9SDimitry Andric if (OtherInnerPtr) { 787e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); 78881ad6265SDimitry Andric 78981ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 790e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 791e8d8bef9SDimitry Andric bool IsArgValNull = OtherInnerPtr->isZeroConstant(); 792e8d8bef9SDimitry Andric 793e8d8bef9SDimitry Andric C.addTransition( 794e8d8bef9SDimitry Andric State, 795e8d8bef9SDimitry Andric C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( 796e8d8bef9SDimitry Andric PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 797e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 798e8d8bef9SDimitry Andric return; 799e8d8bef9SDimitry Andric if (BR.isInteresting(OtherSmartPtrRegion)) { 800e8d8bef9SDimitry Andric OS << "Smart pointer"; 801e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 802e8d8bef9SDimitry Andric OS << " is null after being moved to"; 803e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 804e8d8bef9SDimitry Andric } 805e8d8bef9SDimitry Andric if (BR.isInteresting(ThisRegion) && IsArgValNull) { 806e8d8bef9SDimitry Andric OS << "A null pointer value is moved to"; 807e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 808e8d8bef9SDimitry Andric BR.markInteresting(OtherSmartPtrRegion); 809e8d8bef9SDimitry Andric } 810e8d8bef9SDimitry Andric })); 811e8d8bef9SDimitry Andric return true; 812e8d8bef9SDimitry Andric } else { 813e8d8bef9SDimitry Andric // In case we dont know anything about value we are moving from 814e8d8bef9SDimitry Andric // remove the entry from map for which smart pointer got moved to. 81581ad6265SDimitry Andric // For unique_ptr<A>, Ty will be 'A*'. 81681ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 817e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(ThisRegion); 818e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 819e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, 820e8d8bef9SDimitry Andric ThisRegion](PathSensitiveBugReport &BR, 821e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 822e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 823e8d8bef9SDimitry Andric !BR.isInteresting(OtherSmartPtrRegion)) 824e8d8bef9SDimitry Andric return; 825e8d8bef9SDimitry Andric OS << "Smart pointer"; 826e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 827e8d8bef9SDimitry Andric OS << " is null after; previous value moved to"; 828e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 829e8d8bef9SDimitry Andric })); 830e8d8bef9SDimitry Andric return true; 831e8d8bef9SDimitry Andric } 832e8d8bef9SDimitry Andric return false; 833e8d8bef9SDimitry Andric } 834e8d8bef9SDimitry Andric 835e8d8bef9SDimitry Andric void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, 836e8d8bef9SDimitry Andric CheckerContext &C) const { 837e8d8bef9SDimitry Andric // To model unique_ptr::operator bool 838e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 839e8d8bef9SDimitry Andric const Expr *CallExpr = Call.getOriginExpr(); 840e8d8bef9SDimitry Andric const MemRegion *ThisRegion = 841e8d8bef9SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 842e8d8bef9SDimitry Andric 84381ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 84481ad6265SDimitry Andric 845e8d8bef9SDimitry Andric SVal InnerPointerVal; 846e8d8bef9SDimitry Andric if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { 847e8d8bef9SDimitry Andric InnerPointerVal = *InnerValPtr; 848e8d8bef9SDimitry Andric } else { 849e8d8bef9SDimitry Andric // In case of inner pointer SVal is not available we create 850e8d8bef9SDimitry Andric // conjureSymbolVal for inner pointer value. 851e8d8bef9SDimitry Andric auto InnerPointerType = getInnerPointerType(Call, C); 852e8d8bef9SDimitry Andric if (InnerPointerType.isNull()) 853e8d8bef9SDimitry Andric return; 854e8d8bef9SDimitry Andric 855e8d8bef9SDimitry Andric const LocationContext *LC = C.getLocationContext(); 856e8d8bef9SDimitry Andric InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 857e8d8bef9SDimitry Andric CallExpr, LC, InnerPointerType, C.blockCount()); 858e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 859e8d8bef9SDimitry Andric } 860e8d8bef9SDimitry Andric 861e8d8bef9SDimitry Andric if (State->isNull(InnerPointerVal).isConstrainedTrue()) { 862e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(), 863e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false)); 864e8d8bef9SDimitry Andric 865e8d8bef9SDimitry Andric C.addTransition(State); 866e8d8bef9SDimitry Andric return; 867e8d8bef9SDimitry Andric } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { 868e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(), 869e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true)); 870e8d8bef9SDimitry Andric 871e8d8bef9SDimitry Andric C.addTransition(State); 872e8d8bef9SDimitry Andric return; 873e8d8bef9SDimitry Andric } else if (move::isMovedFrom(State, ThisRegion)) { 874e8d8bef9SDimitry Andric C.addTransition( 875e8d8bef9SDimitry Andric State->BindExpr(CallExpr, C.getLocationContext(), 876e8d8bef9SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType()))); 877e8d8bef9SDimitry Andric return; 878e8d8bef9SDimitry Andric } else { 879e8d8bef9SDimitry Andric ProgramStateRef NotNullState, NullState; 880e8d8bef9SDimitry Andric std::tie(NotNullState, NullState) = 881e8d8bef9SDimitry Andric State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); 882e8d8bef9SDimitry Andric 88381ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 884e8d8bef9SDimitry Andric // Explicitly tracking the region as null. 885e8d8bef9SDimitry Andric NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); 886e8d8bef9SDimitry Andric 887e8d8bef9SDimitry Andric NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), 888e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false)); 889e8d8bef9SDimitry Andric C.addTransition(NullState, C.getNoteTag( 890e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR, 891e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 892e8d8bef9SDimitry Andric OS << "Assuming smart pointer"; 893e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 894e8d8bef9SDimitry Andric OS << " is null"; 895e8d8bef9SDimitry Andric }, 896e8d8bef9SDimitry Andric /*IsPrunable=*/true)); 897e8d8bef9SDimitry Andric NotNullState = 898e8d8bef9SDimitry Andric NotNullState->BindExpr(CallExpr, C.getLocationContext(), 899e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true)); 900e8d8bef9SDimitry Andric C.addTransition( 901e8d8bef9SDimitry Andric NotNullState, 902e8d8bef9SDimitry Andric C.getNoteTag( 903e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 904e8d8bef9SDimitry Andric OS << "Assuming smart pointer"; 905e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 906e8d8bef9SDimitry Andric OS << " is non-null"; 907e8d8bef9SDimitry Andric }, 908e8d8bef9SDimitry Andric /*IsPrunable=*/true)); 909e8d8bef9SDimitry Andric return; 910e8d8bef9SDimitry Andric } 9115ffd83dbSDimitry Andric } 9125ffd83dbSDimitry Andric 9135ffd83dbSDimitry Andric void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 9145ffd83dbSDimitry Andric auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 9155ffd83dbSDimitry Andric Checker->ModelSmartPtrDereference = 9165ffd83dbSDimitry Andric Mgr.getAnalyzerOptions().getCheckerBooleanOption( 9175ffd83dbSDimitry Andric Checker, "ModelSmartPtrDereference"); 9185ffd83dbSDimitry Andric } 9195ffd83dbSDimitry Andric 9205ffd83dbSDimitry Andric bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 9215ffd83dbSDimitry Andric const LangOptions &LO = mgr.getLangOpts(); 9220b57cec5SDimitry Andric return LO.CPlusPlus; 9230b57cec5SDimitry Andric } 924