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