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" 34*06c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h" 35fe6060f1SDimitry Andric #include "llvm/ADT/StringMap.h" 36fe6060f1SDimitry Andric #include "llvm/Support/ErrorHandling.h" 37bdd1243dSDimitry Andric #include <optional> 38e8d8bef9SDimitry Andric #include <string> 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric using namespace clang; 410b57cec5SDimitry Andric using namespace ento; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric namespace { 44fe6060f1SDimitry Andric 45e8d8bef9SDimitry Andric class SmartPtrModeling 46e8d8bef9SDimitry Andric : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, 47e8d8bef9SDimitry Andric check::LiveSymbols> { 485ffd83dbSDimitry Andric 49e8d8bef9SDimitry Andric bool isBoolConversionMethod(const CallEvent &Call) const; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric public: 525ffd83dbSDimitry Andric // Whether the checker should model for null dereferences of smart pointers. 5381ad6265SDimitry Andric bool ModelSmartPtrDereference = false; 540b57cec5SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 555ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 56e8d8bef9SDimitry Andric ProgramStateRef 57e8d8bef9SDimitry Andric checkRegionChanges(ProgramStateRef State, 58e8d8bef9SDimitry Andric const InvalidatedSymbols *Invalidated, 59e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 60e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, 61e8d8bef9SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const; 62e8d8bef9SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 63e8d8bef9SDimitry Andric const char *Sep) const override; 64e8d8bef9SDimitry Andric void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 655ffd83dbSDimitry Andric 665ffd83dbSDimitry Andric private: 675ffd83dbSDimitry Andric void handleReset(const CallEvent &Call, CheckerContext &C) const; 685ffd83dbSDimitry Andric void handleRelease(const CallEvent &Call, CheckerContext &C) const; 69fe6060f1SDimitry Andric void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; 70e8d8bef9SDimitry Andric void handleGet(const CallEvent &Call, CheckerContext &C) const; 71e8d8bef9SDimitry Andric bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; 72e8d8bef9SDimitry Andric bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, 73e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const; 74e8d8bef9SDimitry Andric bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, 7581ad6265SDimitry Andric const MemRegion *OtherSmartPtrRegion, 7681ad6265SDimitry Andric const CallEvent &Call) const; 77e8d8bef9SDimitry Andric void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; 78fe6060f1SDimitry Andric bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; 79fe6060f1SDimitry Andric bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; 80fe6060f1SDimitry Andric bool handleSwap(ProgramStateRef State, SVal First, SVal Second, 81fe6060f1SDimitry Andric CheckerContext &C) const; 82fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> 83fe6060f1SDimitry Andric retrieveOrConjureInnerPtrVal(ProgramStateRef State, 84fe6060f1SDimitry Andric const MemRegion *ThisRegion, const Expr *E, 85fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const; 865ffd83dbSDimitry Andric 875ffd83dbSDimitry Andric using SmartPtrMethodHandlerFn = 885ffd83dbSDimitry Andric void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; 895ffd83dbSDimitry Andric CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ 90bdd1243dSDimitry Andric {{{"reset"}}, &SmartPtrModeling::handleReset}, 91bdd1243dSDimitry Andric {{{"release"}}, &SmartPtrModeling::handleRelease}, 92bdd1243dSDimitry Andric {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, 93bdd1243dSDimitry Andric {{{"get"}}, &SmartPtrModeling::handleGet}}; 94fe6060f1SDimitry Andric const CallDescription StdSwapCall{{"std", "swap"}, 2}; 95fe6060f1SDimitry Andric const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; 96fe6060f1SDimitry Andric const CallDescription StdMakeUniqueForOverwriteCall{ 97fe6060f1SDimitry Andric {"std", "make_unique_for_overwrite"}}; 980b57cec5SDimitry Andric }; 990b57cec5SDimitry Andric } // end of anonymous namespace 1000b57cec5SDimitry Andric 1015ffd83dbSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 1025ffd83dbSDimitry Andric 103fe6060f1SDimitry Andric // Checks if RD has name in Names and is in std namespace 104fe6060f1SDimitry Andric static bool hasStdClassWithName(const CXXRecordDecl *RD, 105fe6060f1SDimitry Andric ArrayRef<llvm::StringLiteral> Names) { 106fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 107fe6060f1SDimitry Andric return false; 108349cc55cSDimitry Andric if (RD->getDeclName().isIdentifier()) 109349cc55cSDimitry Andric return llvm::is_contained(Names, RD->getName()); 110fe6060f1SDimitry Andric return false; 111fe6060f1SDimitry Andric } 112fe6060f1SDimitry Andric 113fe6060f1SDimitry Andric constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr", 114fe6060f1SDimitry Andric "weak_ptr"}; 115fe6060f1SDimitry Andric 116fe6060f1SDimitry Andric static bool isStdSmartPtr(const CXXRecordDecl *RD) { 117fe6060f1SDimitry Andric return hasStdClassWithName(RD, STD_PTR_NAMES); 118fe6060f1SDimitry Andric } 119fe6060f1SDimitry Andric 120fe6060f1SDimitry Andric static bool isStdSmartPtr(const Expr *E) { 121fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 122fe6060f1SDimitry Andric } 123fe6060f1SDimitry Andric 1245ffd83dbSDimitry Andric // Define the inter-checker API. 1255ffd83dbSDimitry Andric namespace clang { 1265ffd83dbSDimitry Andric namespace ento { 1275ffd83dbSDimitry Andric namespace smartptr { 1285ffd83dbSDimitry Andric bool isStdSmartPtrCall(const CallEvent &Call) { 1295ffd83dbSDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 1305ffd83dbSDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 1315ffd83dbSDimitry Andric return false; 132fe6060f1SDimitry Andric return isStdSmartPtr(MethodDecl->getParent()); 133fe6060f1SDimitry Andric } 1345ffd83dbSDimitry Andric 135fe6060f1SDimitry Andric bool isStdSmartPtr(const CXXRecordDecl *RD) { 136fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 1375ffd83dbSDimitry Andric return false; 1385ffd83dbSDimitry Andric 139fe6060f1SDimitry Andric if (RD->getDeclName().isIdentifier()) { 140fe6060f1SDimitry Andric StringRef Name = RD->getName(); 1415ffd83dbSDimitry Andric return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 1425ffd83dbSDimitry Andric } 1435ffd83dbSDimitry Andric return false; 1445ffd83dbSDimitry Andric } 1455ffd83dbSDimitry Andric 146fe6060f1SDimitry Andric bool isStdSmartPtr(const Expr *E) { 147fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 148fe6060f1SDimitry Andric } 149fe6060f1SDimitry Andric 1505ffd83dbSDimitry Andric bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 1515ffd83dbSDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 152e8d8bef9SDimitry Andric return InnerPointVal && 153e8d8bef9SDimitry Andric !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); 1545ffd83dbSDimitry Andric } 1555ffd83dbSDimitry Andric } // namespace smartptr 1565ffd83dbSDimitry Andric } // namespace ento 1575ffd83dbSDimitry Andric } // namespace clang 1585ffd83dbSDimitry Andric 159e8d8bef9SDimitry Andric // If a region is removed all of the subregions need to be removed too. 160e8d8bef9SDimitry Andric static TrackedRegionMapTy 161e8d8bef9SDimitry Andric removeTrackedSubregions(TrackedRegionMapTy RegionMap, 162e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory, 163e8d8bef9SDimitry Andric const MemRegion *Region) { 164e8d8bef9SDimitry Andric if (!Region) 165e8d8bef9SDimitry Andric return RegionMap; 166e8d8bef9SDimitry Andric for (const auto &E : RegionMap) { 167e8d8bef9SDimitry Andric if (E.first->isSubRegionOf(Region)) 168e8d8bef9SDimitry Andric RegionMap = RegionMapFactory.remove(RegionMap, E.first); 169e8d8bef9SDimitry Andric } 170e8d8bef9SDimitry Andric return RegionMap; 171e8d8bef9SDimitry Andric } 172e8d8bef9SDimitry Andric 173e8d8bef9SDimitry Andric static ProgramStateRef updateSwappedRegion(ProgramStateRef State, 174e8d8bef9SDimitry Andric const MemRegion *Region, 175e8d8bef9SDimitry Andric const SVal *RegionInnerPointerVal) { 176e8d8bef9SDimitry Andric if (RegionInnerPointerVal) { 177e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); 178e8d8bef9SDimitry Andric } else { 179e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(Region); 180e8d8bef9SDimitry Andric } 181e8d8bef9SDimitry Andric return State; 182e8d8bef9SDimitry Andric } 183e8d8bef9SDimitry Andric 184fe6060f1SDimitry Andric static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { 185fe6060f1SDimitry Andric if (!RD || !RD->isInStdNamespace()) 186fe6060f1SDimitry Andric return {}; 187fe6060f1SDimitry Andric 188fe6060f1SDimitry Andric const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); 189fe6060f1SDimitry Andric if (!TSD) 190fe6060f1SDimitry Andric return {}; 191fe6060f1SDimitry Andric 192fe6060f1SDimitry Andric auto TemplateArgs = TSD->getTemplateArgs().asArray(); 193fe6060f1SDimitry Andric if (TemplateArgs.empty()) 194fe6060f1SDimitry Andric return {}; 195fe6060f1SDimitry Andric auto InnerValueType = TemplateArgs[0].getAsType(); 196fe6060f1SDimitry Andric return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); 197fe6060f1SDimitry Andric } 198fe6060f1SDimitry Andric 199fe6060f1SDimitry Andric // This is for use with standalone-functions like std::make_unique, 200fe6060f1SDimitry Andric // std::make_unique_for_overwrite, etc. It reads the template parameter and 201fe6060f1SDimitry Andric // returns the pointer type corresponding to it, 202fe6060f1SDimitry Andric static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, 203fe6060f1SDimitry Andric CheckerContext &C) { 204fe6060f1SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 205fe6060f1SDimitry Andric if (!FD || !FD->isFunctionTemplateSpecialization()) 206fe6060f1SDimitry Andric return {}; 207fe6060f1SDimitry Andric const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); 208fe6060f1SDimitry Andric if (TemplateArgs.size() == 0) 209fe6060f1SDimitry Andric return {}; 210fe6060f1SDimitry Andric auto ValueType = TemplateArgs[0].getAsType(); 211fe6060f1SDimitry Andric return C.getASTContext().getPointerType(ValueType.getCanonicalType()); 212fe6060f1SDimitry Andric } 213fe6060f1SDimitry Andric 214e8d8bef9SDimitry Andric // Helper method to get the inner pointer type of specialized smart pointer 215e8d8bef9SDimitry Andric // Returns empty type if not found valid inner pointer type. 216e8d8bef9SDimitry Andric static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { 217e8d8bef9SDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 218e8d8bef9SDimitry Andric if (!MethodDecl || !MethodDecl->getParent()) 219e8d8bef9SDimitry Andric return {}; 220e8d8bef9SDimitry Andric 221e8d8bef9SDimitry Andric const auto *RecordDecl = MethodDecl->getParent(); 222fe6060f1SDimitry Andric return getInnerPointerType(C, RecordDecl); 223e8d8bef9SDimitry Andric } 224e8d8bef9SDimitry Andric 225e8d8bef9SDimitry Andric // Helper method to pretty print region and avoid extra spacing. 226e8d8bef9SDimitry Andric static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, 227e8d8bef9SDimitry Andric const MemRegion *Region) { 228e8d8bef9SDimitry Andric if (Region->canPrintPretty()) { 229e8d8bef9SDimitry Andric OS << " "; 230e8d8bef9SDimitry Andric Region->printPretty(OS); 231e8d8bef9SDimitry Andric } 232e8d8bef9SDimitry Andric } 233e8d8bef9SDimitry Andric 234e8d8bef9SDimitry Andric bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { 2350b57cec5SDimitry Andric // TODO: Update CallDescription to support anonymous calls? 2360b57cec5SDimitry Andric // TODO: Handle other methods, such as .get() or .release(). 2370b57cec5SDimitry Andric // But once we do, we'd need a visitor to explain null dereferences 2380b57cec5SDimitry Andric // that are found via such modeling. 2390b57cec5SDimitry Andric const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 2400b57cec5SDimitry Andric return CD && CD->getConversionType()->isBooleanType(); 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric 243fe6060f1SDimitry Andric constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"}; 244fe6060f1SDimitry Andric 245fe6060f1SDimitry Andric bool isStdBasicOstream(const Expr *E) { 246fe6060f1SDimitry Andric const auto *RD = E->getType()->getAsCXXRecordDecl(); 247fe6060f1SDimitry Andric return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES); 248fe6060f1SDimitry Andric } 249fe6060f1SDimitry Andric 250fe6060f1SDimitry Andric static bool isStdFunctionCall(const CallEvent &Call) { 251fe6060f1SDimitry Andric return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace(); 252fe6060f1SDimitry Andric } 253fe6060f1SDimitry Andric 254fe6060f1SDimitry Andric bool isStdOstreamOperatorCall(const CallEvent &Call) { 255fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 256fe6060f1SDimitry Andric return false; 257fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 258fe6060f1SDimitry Andric if (!FC) 259fe6060f1SDimitry Andric return false; 260fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 261fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 262fe6060f1SDimitry Andric return false; 263fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 264fe6060f1SDimitry Andric if (OOK != clang::OO_LessLess) 265fe6060f1SDimitry Andric return false; 266fe6060f1SDimitry Andric return isStdSmartPtr(Call.getArgExpr(1)) && 267fe6060f1SDimitry Andric isStdBasicOstream(Call.getArgExpr(0)); 268fe6060f1SDimitry Andric } 269fe6060f1SDimitry Andric 270fe6060f1SDimitry Andric static bool isPotentiallyComparisionOpCall(const CallEvent &Call) { 271fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 272fe6060f1SDimitry Andric return false; 273fe6060f1SDimitry Andric return smartptr::isStdSmartPtr(Call.getArgExpr(0)) || 274fe6060f1SDimitry Andric smartptr::isStdSmartPtr(Call.getArgExpr(1)); 275fe6060f1SDimitry Andric } 276fe6060f1SDimitry Andric 2770b57cec5SDimitry Andric bool SmartPtrModeling::evalCall(const CallEvent &Call, 2780b57cec5SDimitry Andric CheckerContext &C) const { 279fe6060f1SDimitry Andric 280e8d8bef9SDimitry Andric ProgramStateRef State = C.getState(); 281fe6060f1SDimitry Andric 282fe6060f1SDimitry Andric // If any one of the arg is a unique_ptr, then 283fe6060f1SDimitry Andric // we can try this function 284fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call)) 285fe6060f1SDimitry Andric if (handleComparisionOp(Call, C)) 286fe6060f1SDimitry Andric return true; 287fe6060f1SDimitry Andric 288fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) 289fe6060f1SDimitry Andric return handleOstreamOperator(Call, C); 290fe6060f1SDimitry Andric 291349cc55cSDimitry Andric if (StdSwapCall.matches(Call)) { 292fe6060f1SDimitry Andric // Check the first arg, if it is of std::unique_ptr type. 293fe6060f1SDimitry Andric assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); 294fe6060f1SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0); 295fe6060f1SDimitry Andric if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) 296fe6060f1SDimitry Andric return false; 297fe6060f1SDimitry Andric return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); 298fe6060f1SDimitry Andric } 299fe6060f1SDimitry Andric 300349cc55cSDimitry Andric if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { 301fe6060f1SDimitry Andric if (!ModelSmartPtrDereference) 302fe6060f1SDimitry Andric return false; 303fe6060f1SDimitry Andric 304bdd1243dSDimitry Andric const std::optional<SVal> ThisRegionOpt = 305bdd1243dSDimitry Andric Call.getReturnValueUnderConstruction(); 306fe6060f1SDimitry Andric if (!ThisRegionOpt) 307fe6060f1SDimitry Andric return false; 308fe6060f1SDimitry Andric 309fe6060f1SDimitry Andric const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( 310fe6060f1SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 311fe6060f1SDimitry Andric getPointerTypeFromTemplateArg(Call, C), C.blockCount()); 312fe6060f1SDimitry Andric 313fe6060f1SDimitry Andric const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); 314fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); 315fe6060f1SDimitry Andric State = State->assume(PtrVal, true); 316fe6060f1SDimitry Andric 317fe6060f1SDimitry Andric // TODO: ExprEngine should do this for us. 318fe6060f1SDimitry Andric // For a bit more context: 319fe6060f1SDimitry Andric // 1) Why do we need this? Since we are modelling a "function" 320fe6060f1SDimitry Andric // that returns a constructed object we need to store this information in 321fe6060f1SDimitry Andric // the program state. 322fe6060f1SDimitry Andric // 323fe6060f1SDimitry Andric // 2) Why does this work? 324fe6060f1SDimitry Andric // `updateObjectsUnderConstruction` does exactly as it sounds. 325fe6060f1SDimitry Andric // 326fe6060f1SDimitry Andric // 3) How should it look like when moved to the Engine? 327fe6060f1SDimitry Andric // It would be nice if we can just 328fe6060f1SDimitry Andric // pretend we don't need to know about this - ie, completely automatic work. 329fe6060f1SDimitry Andric // However, realistically speaking, I think we would need to "signal" the 330fe6060f1SDimitry Andric // ExprEngine evalCall handler that we are constructing an object with this 331fe6060f1SDimitry Andric // function call (constructors obviously construct, hence can be 332fe6060f1SDimitry Andric // automatically deduced). 333fe6060f1SDimitry Andric auto &Engine = State->getStateManager().getOwningEngine(); 334fe6060f1SDimitry Andric State = Engine.updateObjectsUnderConstruction( 335fe6060f1SDimitry Andric *ThisRegionOpt, nullptr, State, C.getLocationContext(), 336fe6060f1SDimitry Andric Call.getConstructionContext(), {}); 337fe6060f1SDimitry Andric 338fe6060f1SDimitry Andric // We don't leave a note here since it is guaranteed the 339fe6060f1SDimitry Andric // unique_ptr from this call is non-null (hence is safe to de-reference). 340fe6060f1SDimitry Andric C.addTransition(State); 341fe6060f1SDimitry Andric return true; 342fe6060f1SDimitry Andric } 343fe6060f1SDimitry Andric 3445ffd83dbSDimitry Andric if (!smartptr::isStdSmartPtrCall(Call)) 3450b57cec5SDimitry Andric return false; 3460b57cec5SDimitry Andric 347e8d8bef9SDimitry Andric if (isBoolConversionMethod(Call)) { 3480b57cec5SDimitry Andric const MemRegion *ThisR = 3490b57cec5SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 3500b57cec5SDimitry Andric 351e8d8bef9SDimitry Andric if (ModelSmartPtrDereference) { 352e8d8bef9SDimitry Andric // The check for the region is moved is duplicated in handleBoolOperation 353e8d8bef9SDimitry Andric // method. 354e8d8bef9SDimitry Andric // FIXME: Once we model std::move for smart pointers clean up this and use 355e8d8bef9SDimitry Andric // that modeling. 356e8d8bef9SDimitry Andric handleBoolConversion(Call, C); 357e8d8bef9SDimitry Andric return true; 358e8d8bef9SDimitry Andric } else { 3590b57cec5SDimitry Andric if (!move::isMovedFrom(State, ThisR)) { 360e8d8bef9SDimitry Andric // TODO: Model this case as well. At least, avoid invalidation of 361e8d8bef9SDimitry Andric // globals. 3620b57cec5SDimitry Andric return false; 3630b57cec5SDimitry Andric } 3640b57cec5SDimitry Andric 3650b57cec5SDimitry Andric // TODO: Add a note to bug reports describing this decision. 366e8d8bef9SDimitry Andric C.addTransition(State->BindExpr( 367e8d8bef9SDimitry Andric Call.getOriginExpr(), C.getLocationContext(), 3680b57cec5SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType()))); 369e8d8bef9SDimitry Andric 3700b57cec5SDimitry Andric return true; 3710b57cec5SDimitry Andric } 372e8d8bef9SDimitry Andric } 3730b57cec5SDimitry Andric 3745ffd83dbSDimitry Andric if (!ModelSmartPtrDereference) 3755ffd83dbSDimitry Andric return false; 3765ffd83dbSDimitry Andric 3775ffd83dbSDimitry Andric if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 378e8d8bef9SDimitry Andric if (CC->getDecl()->isCopyConstructor()) 3795ffd83dbSDimitry Andric return false; 3805ffd83dbSDimitry Andric 381e8d8bef9SDimitry Andric const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 382e8d8bef9SDimitry Andric if (!ThisRegion) 3835ffd83dbSDimitry Andric return false; 3845ffd83dbSDimitry Andric 38581ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 38681ad6265SDimitry Andric 387e8d8bef9SDimitry Andric if (CC->getDecl()->isMoveConstructor()) 388e8d8bef9SDimitry Andric return handleMoveCtr(Call, C, ThisRegion); 389e8d8bef9SDimitry Andric 390e8d8bef9SDimitry Andric if (Call.getNumArgs() == 0) { 39181ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 392e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 393e8d8bef9SDimitry Andric 394e8d8bef9SDimitry Andric C.addTransition( 395e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 396e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 397e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 398e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 399e8d8bef9SDimitry Andric return; 400e8d8bef9SDimitry Andric OS << "Default constructed smart pointer"; 401e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 402e8d8bef9SDimitry Andric OS << " is null"; 403e8d8bef9SDimitry Andric })); 404e8d8bef9SDimitry Andric } else { 405e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0); 406e8d8bef9SDimitry Andric assert(TrackingExpr->getType()->isPointerType() && 407e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap"); 408e8d8bef9SDimitry Andric auto ArgVal = Call.getArgSVal(0); 409e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 410e8d8bef9SDimitry Andric 411e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 412e8d8bef9SDimitry Andric ArgVal](PathSensitiveBugReport &BR, 413e8d8bef9SDimitry Andric llvm::raw_ostream &OS) { 414e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 415e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion)) 416e8d8bef9SDimitry Andric return; 417e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 418e8d8bef9SDimitry Andric OS << "Smart pointer"; 419e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion); 420e8d8bef9SDimitry Andric if (ArgVal.isZeroConstant()) 421e8d8bef9SDimitry Andric OS << " is constructed using a null value"; 422e8d8bef9SDimitry Andric else 423e8d8bef9SDimitry Andric OS << " is constructed"; 424e8d8bef9SDimitry Andric })); 425e8d8bef9SDimitry Andric } 4265ffd83dbSDimitry Andric return true; 4270b57cec5SDimitry Andric } 4280b57cec5SDimitry Andric 429e8d8bef9SDimitry Andric if (handleAssignOp(Call, C)) 430e8d8bef9SDimitry Andric return true; 431e8d8bef9SDimitry Andric 4325ffd83dbSDimitry Andric const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 4335ffd83dbSDimitry Andric if (!Handler) 4345ffd83dbSDimitry Andric return false; 4355ffd83dbSDimitry Andric (this->**Handler)(Call, C); 4365ffd83dbSDimitry Andric 4375ffd83dbSDimitry Andric return C.isDifferent(); 4385ffd83dbSDimitry Andric } 4395ffd83dbSDimitry Andric 440fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( 441fe6060f1SDimitry Andric ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E, 442fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const { 443fe6060f1SDimitry Andric const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); 444fe6060f1SDimitry Andric if (Ptr) 445fe6060f1SDimitry Andric return {*Ptr, State}; 446fe6060f1SDimitry Andric auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(), 447fe6060f1SDimitry Andric Type, C.blockCount()); 448fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Val); 449fe6060f1SDimitry Andric return {Val, State}; 450fe6060f1SDimitry Andric } 451fe6060f1SDimitry Andric 452fe6060f1SDimitry Andric bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, 453fe6060f1SDimitry Andric CheckerContext &C) const { 454fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 455fe6060f1SDimitry Andric if (!FC) 456fe6060f1SDimitry Andric return false; 457fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl(); 458fe6060f1SDimitry Andric if (!FD->isOverloadedOperator()) 459fe6060f1SDimitry Andric return false; 460fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 461fe6060f1SDimitry Andric if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || 462fe6060f1SDimitry Andric OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || 463fe6060f1SDimitry Andric OOK == OO_Spaceship)) 464fe6060f1SDimitry Andric return false; 465fe6060f1SDimitry Andric 466fe6060f1SDimitry Andric // There are some special cases about which we can infer about 467fe6060f1SDimitry Andric // the resulting answer. 468fe6060f1SDimitry Andric // For reference, there is a discussion at https://reviews.llvm.org/D104616. 469fe6060f1SDimitry Andric // Also, the cppreference page is good to look at 470fe6060f1SDimitry Andric // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. 471fe6060f1SDimitry Andric 472fe6060f1SDimitry Andric auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, 473fe6060f1SDimitry Andric SVal S) -> std::pair<SVal, ProgramStateRef> { 474fe6060f1SDimitry Andric if (S.isZeroConstant()) { 475fe6060f1SDimitry Andric return {S, State}; 476fe6060f1SDimitry Andric } 477fe6060f1SDimitry Andric const MemRegion *Reg = S.getAsRegion(); 478fe6060f1SDimitry Andric assert(Reg && 479fe6060f1SDimitry Andric "this pointer of std::unique_ptr should be obtainable as MemRegion"); 480fe6060f1SDimitry Andric QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); 481fe6060f1SDimitry Andric return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C); 482fe6060f1SDimitry Andric }; 483fe6060f1SDimitry Andric 484fe6060f1SDimitry Andric SVal First = Call.getArgSVal(0); 485fe6060f1SDimitry Andric SVal Second = Call.getArgSVal(1); 486fe6060f1SDimitry Andric const auto *FirstExpr = Call.getArgExpr(0); 487fe6060f1SDimitry Andric const auto *SecondExpr = Call.getArgExpr(1); 488fe6060f1SDimitry Andric 489fe6060f1SDimitry Andric const auto *ResultExpr = Call.getOriginExpr(); 490fe6060f1SDimitry Andric const auto *LCtx = C.getLocationContext(); 491fe6060f1SDimitry Andric auto &Bldr = C.getSValBuilder(); 492fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 493fe6060f1SDimitry Andric 494fe6060f1SDimitry Andric SVal FirstPtrVal, SecondPtrVal; 495fe6060f1SDimitry Andric std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First); 496fe6060f1SDimitry Andric std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second); 497fe6060f1SDimitry Andric BinaryOperatorKind BOK = 498fe6060f1SDimitry Andric operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe(); 499fe6060f1SDimitry Andric auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal, 500fe6060f1SDimitry Andric Call.getResultType()); 501fe6060f1SDimitry Andric 502fe6060f1SDimitry Andric if (OOK != OO_Spaceship) { 503fe6060f1SDimitry Andric ProgramStateRef TrueState, FalseState; 504fe6060f1SDimitry Andric std::tie(TrueState, FalseState) = 505fe6060f1SDimitry Andric State->assume(*RetVal.getAs<DefinedOrUnknownSVal>()); 506fe6060f1SDimitry Andric if (TrueState) 507fe6060f1SDimitry Andric C.addTransition( 508fe6060f1SDimitry Andric TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true))); 509fe6060f1SDimitry Andric if (FalseState) 510fe6060f1SDimitry Andric C.addTransition( 511fe6060f1SDimitry Andric FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false))); 512fe6060f1SDimitry Andric } else { 513fe6060f1SDimitry Andric C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal)); 514fe6060f1SDimitry Andric } 515fe6060f1SDimitry Andric return true; 516fe6060f1SDimitry Andric } 517fe6060f1SDimitry Andric 518fe6060f1SDimitry Andric bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call, 519fe6060f1SDimitry Andric CheckerContext &C) const { 520fe6060f1SDimitry Andric // operator<< does not modify the smart pointer. 521fe6060f1SDimitry Andric // And we don't really have much of modelling of basic_ostream. 522fe6060f1SDimitry Andric // So, we are better off: 523fe6060f1SDimitry Andric // 1) Invalidating the mem-region of the ostream object at hand. 524fe6060f1SDimitry Andric // 2) Setting the SVal of the basic_ostream as the return value. 525fe6060f1SDimitry Andric // Not very satisfying, but it gets the job done, and is better 526fe6060f1SDimitry Andric // than the default handling. :) 527fe6060f1SDimitry Andric 528fe6060f1SDimitry Andric ProgramStateRef State = C.getState(); 529fe6060f1SDimitry Andric const auto StreamVal = Call.getArgSVal(0); 530fe6060f1SDimitry Andric const MemRegion *StreamThisRegion = StreamVal.getAsRegion(); 531fe6060f1SDimitry Andric if (!StreamThisRegion) 532fe6060f1SDimitry Andric return false; 533fe6060f1SDimitry Andric State = 534fe6060f1SDimitry Andric State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(), 535fe6060f1SDimitry Andric C.blockCount(), C.getLocationContext(), false); 536fe6060f1SDimitry Andric State = 537fe6060f1SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal); 538fe6060f1SDimitry Andric C.addTransition(State); 539fe6060f1SDimitry Andric return true; 540fe6060f1SDimitry Andric } 541fe6060f1SDimitry Andric 5425ffd83dbSDimitry Andric void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 5435ffd83dbSDimitry Andric CheckerContext &C) const { 5445ffd83dbSDimitry Andric ProgramStateRef State = C.getState(); 5455ffd83dbSDimitry Andric // Clean up dead regions from the region map. 5465ffd83dbSDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 5475ffd83dbSDimitry Andric for (auto E : TrackedRegions) { 5485ffd83dbSDimitry Andric const MemRegion *Region = E.first; 5495ffd83dbSDimitry Andric bool IsRegDead = !SymReaper.isLiveRegion(Region); 5505ffd83dbSDimitry Andric 5515ffd83dbSDimitry Andric if (IsRegDead) 5525ffd83dbSDimitry Andric State = State->remove<TrackedRegionMap>(Region); 5535ffd83dbSDimitry Andric } 5545ffd83dbSDimitry Andric C.addTransition(State); 5555ffd83dbSDimitry Andric } 5565ffd83dbSDimitry Andric 557e8d8bef9SDimitry Andric void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, 558e8d8bef9SDimitry Andric const char *NL, const char *Sep) const { 559e8d8bef9SDimitry Andric TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 560e8d8bef9SDimitry Andric 561e8d8bef9SDimitry Andric if (!RS.isEmpty()) { 562e8d8bef9SDimitry Andric Out << Sep << "Smart ptr regions :" << NL; 563e8d8bef9SDimitry Andric for (auto I : RS) { 564e8d8bef9SDimitry Andric I.first->dumpToStream(Out); 565e8d8bef9SDimitry Andric if (smartptr::isNullSmartPtr(State, I.first)) 566e8d8bef9SDimitry Andric Out << ": Null"; 567e8d8bef9SDimitry Andric else 568e8d8bef9SDimitry Andric Out << ": Non Null"; 569e8d8bef9SDimitry Andric Out << NL; 570e8d8bef9SDimitry Andric } 571e8d8bef9SDimitry Andric } 572e8d8bef9SDimitry Andric } 573e8d8bef9SDimitry Andric 574e8d8bef9SDimitry Andric ProgramStateRef SmartPtrModeling::checkRegionChanges( 575e8d8bef9SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated, 576e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions, 577e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 578e8d8bef9SDimitry Andric const CallEvent *Call) const { 579e8d8bef9SDimitry Andric TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); 580e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory = 581e8d8bef9SDimitry Andric State->get_context<TrackedRegionMap>(); 582e8d8bef9SDimitry Andric for (const auto *Region : Regions) 583e8d8bef9SDimitry Andric RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, 584e8d8bef9SDimitry Andric Region->getBaseRegion()); 585e8d8bef9SDimitry Andric return State->set<TrackedRegionMap>(RegionMap); 586e8d8bef9SDimitry Andric } 587e8d8bef9SDimitry Andric 588e8d8bef9SDimitry Andric void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, 589e8d8bef9SDimitry Andric SymbolReaper &SR) const { 590e8d8bef9SDimitry Andric // Marking tracked symbols alive 591e8d8bef9SDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 592*06c3fb27SDimitry Andric for (SVal Val : llvm::make_second_range(TrackedRegions)) { 593*06c3fb27SDimitry Andric for (SymbolRef Sym : Val.symbols()) { 594*06c3fb27SDimitry Andric SR.markLive(Sym); 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