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