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