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/BugReporter/BugType.h" 24 #include "clang/StaticAnalyzer/Core/Checker.h" 25 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 30 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 31 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 32 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 33 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 34 #include "llvm/ADT/STLExtras.h" 35 #include "llvm/Support/ErrorHandling.h" 36 #include <optional> 37 #include <string> 38 39 using namespace clang; 40 using namespace ento; 41 42 namespace { 43 44 class SmartPtrModeling 45 : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, 46 check::LiveSymbols> { 47 48 bool isBoolConversionMethod(const CallEvent &Call) const; 49 50 public: 51 // Whether the checker should model for null dereferences of smart pointers. 52 bool ModelSmartPtrDereference = false; 53 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 54 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 55 ProgramStateRef 56 checkRegionChanges(ProgramStateRef State, 57 const InvalidatedSymbols *Invalidated, 58 ArrayRef<const MemRegion *> ExplicitRegions, 59 ArrayRef<const MemRegion *> Regions, 60 const LocationContext *LCtx, const CallEvent *Call) const; 61 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 62 const char *Sep) const override; 63 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 64 65 private: 66 void handleReset(const CallEvent &Call, CheckerContext &C) const; 67 void handleRelease(const CallEvent &Call, CheckerContext &C) const; 68 void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; 69 void handleGet(const CallEvent &Call, CheckerContext &C) const; 70 bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; 71 bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, 72 const MemRegion *ThisRegion) const; 73 bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, 74 const MemRegion *OtherSmartPtrRegion, 75 const CallEvent &Call) const; 76 void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; 77 bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; 78 bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; 79 bool handleSwap(ProgramStateRef State, SVal First, SVal Second, 80 CheckerContext &C) const; 81 std::pair<SVal, ProgramStateRef> 82 retrieveOrConjureInnerPtrVal(ProgramStateRef State, 83 const MemRegion *ThisRegion, const Expr *E, 84 QualType Type, CheckerContext &C) const; 85 86 using SmartPtrMethodHandlerFn = 87 void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; 88 CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ 89 {{{"reset"}}, &SmartPtrModeling::handleReset}, 90 {{{"release"}}, &SmartPtrModeling::handleRelease}, 91 {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, 92 {{{"get"}}, &SmartPtrModeling::handleGet}}; 93 const CallDescription StdSwapCall{{"std", "swap"}, 2}; 94 const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; 95 const CallDescription StdMakeUniqueForOverwriteCall{ 96 {"std", "make_unique_for_overwrite"}}; 97 }; 98 } // end of anonymous namespace 99 100 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 101 102 // Checks if RD has name in Names and is in std namespace 103 static bool hasStdClassWithName(const CXXRecordDecl *RD, 104 ArrayRef<llvm::StringLiteral> Names) { 105 if (!RD || !RD->getDeclContext()->isStdNamespace()) 106 return false; 107 if (RD->getDeclName().isIdentifier()) 108 return llvm::is_contained(Names, RD->getName()); 109 return false; 110 } 111 112 constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr", 113 "weak_ptr"}; 114 115 static bool isStdSmartPtr(const CXXRecordDecl *RD) { 116 return hasStdClassWithName(RD, STD_PTR_NAMES); 117 } 118 119 static bool isStdSmartPtr(const Expr *E) { 120 return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 121 } 122 123 // Define the inter-checker API. 124 namespace clang { 125 namespace ento { 126 namespace smartptr { 127 bool isStdSmartPtrCall(const CallEvent &Call) { 128 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 129 if (!MethodDecl || !MethodDecl->getParent()) 130 return false; 131 return isStdSmartPtr(MethodDecl->getParent()); 132 } 133 134 bool isStdSmartPtr(const CXXRecordDecl *RD) { 135 if (!RD || !RD->getDeclContext()->isStdNamespace()) 136 return false; 137 138 if (RD->getDeclName().isIdentifier()) { 139 StringRef Name = RD->getName(); 140 return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 141 } 142 return false; 143 } 144 145 bool isStdSmartPtr(const Expr *E) { 146 return isStdSmartPtr(E->getType()->getAsCXXRecordDecl()); 147 } 148 149 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 150 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 151 return InnerPointVal && 152 !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); 153 } 154 } // namespace smartptr 155 } // namespace ento 156 } // namespace clang 157 158 // If a region is removed all of the subregions need to be removed too. 159 static TrackedRegionMapTy 160 removeTrackedSubregions(TrackedRegionMapTy RegionMap, 161 TrackedRegionMapTy::Factory &RegionMapFactory, 162 const MemRegion *Region) { 163 if (!Region) 164 return RegionMap; 165 for (const auto &E : RegionMap) { 166 if (E.first->isSubRegionOf(Region)) 167 RegionMap = RegionMapFactory.remove(RegionMap, E.first); 168 } 169 return RegionMap; 170 } 171 172 static ProgramStateRef updateSwappedRegion(ProgramStateRef State, 173 const MemRegion *Region, 174 const SVal *RegionInnerPointerVal) { 175 if (RegionInnerPointerVal) { 176 State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); 177 } else { 178 State = State->remove<TrackedRegionMap>(Region); 179 } 180 return State; 181 } 182 183 static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { 184 if (!RD || !RD->isInStdNamespace()) 185 return {}; 186 187 const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD); 188 if (!TSD) 189 return {}; 190 191 auto TemplateArgs = TSD->getTemplateArgs().asArray(); 192 if (TemplateArgs.empty()) 193 return {}; 194 auto InnerValueType = TemplateArgs[0].getAsType(); 195 return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); 196 } 197 198 // This is for use with standalone-functions like std::make_unique, 199 // std::make_unique_for_overwrite, etc. It reads the template parameter and 200 // returns the pointer type corresponding to it, 201 static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, 202 CheckerContext &C) { 203 const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 204 if (!FD || !FD->getPrimaryTemplate()) 205 return {}; 206 const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); 207 if (TemplateArgs.size() == 0) 208 return {}; 209 auto ValueType = TemplateArgs[0].getAsType(); 210 return C.getASTContext().getPointerType(ValueType.getCanonicalType()); 211 } 212 213 // Helper method to get the inner pointer type of specialized smart pointer 214 // Returns empty type if not found valid inner pointer type. 215 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { 216 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 217 if (!MethodDecl || !MethodDecl->getParent()) 218 return {}; 219 220 const auto *RecordDecl = MethodDecl->getParent(); 221 return getInnerPointerType(C, RecordDecl); 222 } 223 224 // Helper method to pretty print region and avoid extra spacing. 225 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, 226 const MemRegion *Region) { 227 if (Region->canPrintPretty()) { 228 OS << " "; 229 Region->printPretty(OS); 230 } 231 } 232 233 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { 234 // TODO: Update CallDescription to support anonymous calls? 235 // TODO: Handle other methods, such as .get() or .release(). 236 // But once we do, we'd need a visitor to explain null dereferences 237 // that are found via such modeling. 238 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 239 return CD && CD->getConversionType()->isBooleanType(); 240 } 241 242 constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"}; 243 244 bool isStdBasicOstream(const Expr *E) { 245 const auto *RD = E->getType()->getAsCXXRecordDecl(); 246 return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES); 247 } 248 249 static bool isStdFunctionCall(const CallEvent &Call) { 250 return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace(); 251 } 252 253 bool isStdOstreamOperatorCall(const CallEvent &Call) { 254 if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 255 return false; 256 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 257 if (!FC) 258 return false; 259 const FunctionDecl *FD = FC->getDecl(); 260 if (!FD->isOverloadedOperator()) 261 return false; 262 const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 263 if (OOK != clang::OO_LessLess) 264 return false; 265 return isStdSmartPtr(Call.getArgExpr(1)) && 266 isStdBasicOstream(Call.getArgExpr(0)); 267 } 268 269 static bool isPotentiallyComparisionOpCall(const CallEvent &Call) { 270 if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call)) 271 return false; 272 return smartptr::isStdSmartPtr(Call.getArgExpr(0)) || 273 smartptr::isStdSmartPtr(Call.getArgExpr(1)); 274 } 275 276 bool SmartPtrModeling::evalCall(const CallEvent &Call, 277 CheckerContext &C) const { 278 279 ProgramStateRef State = C.getState(); 280 281 // If any one of the arg is a unique_ptr, then 282 // we can try this function 283 if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call)) 284 if (handleComparisionOp(Call, C)) 285 return true; 286 287 if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) 288 return handleOstreamOperator(Call, C); 289 290 if (StdSwapCall.matches(Call)) { 291 // Check the first arg, if it is of std::unique_ptr type. 292 assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); 293 const Expr *FirstArg = Call.getArgExpr(0); 294 if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) 295 return false; 296 return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); 297 } 298 299 if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { 300 if (!ModelSmartPtrDereference) 301 return false; 302 303 const std::optional<SVal> ThisRegionOpt = 304 Call.getReturnValueUnderConstruction(); 305 if (!ThisRegionOpt) 306 return false; 307 308 const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( 309 Call.getOriginExpr(), C.getLocationContext(), 310 getPointerTypeFromTemplateArg(Call, C), C.blockCount()); 311 312 const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); 313 State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); 314 State = State->assume(PtrVal, true); 315 316 // TODO: ExprEngine should do this for us. 317 // For a bit more context: 318 // 1) Why do we need this? Since we are modelling a "function" 319 // that returns a constructed object we need to store this information in 320 // the program state. 321 // 322 // 2) Why does this work? 323 // `updateObjectsUnderConstruction` does exactly as it sounds. 324 // 325 // 3) How should it look like when moved to the Engine? 326 // It would be nice if we can just 327 // pretend we don't need to know about this - ie, completely automatic work. 328 // However, realistically speaking, I think we would need to "signal" the 329 // ExprEngine evalCall handler that we are constructing an object with this 330 // function call (constructors obviously construct, hence can be 331 // automatically deduced). 332 auto &Engine = State->getStateManager().getOwningEngine(); 333 State = Engine.updateObjectsUnderConstruction( 334 *ThisRegionOpt, nullptr, State, C.getLocationContext(), 335 Call.getConstructionContext(), {}); 336 337 // We don't leave a note here since it is guaranteed the 338 // unique_ptr from this call is non-null (hence is safe to de-reference). 339 C.addTransition(State); 340 return true; 341 } 342 343 if (!smartptr::isStdSmartPtrCall(Call)) 344 return false; 345 346 if (isBoolConversionMethod(Call)) { 347 const MemRegion *ThisR = 348 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 349 350 if (ModelSmartPtrDereference) { 351 // The check for the region is moved is duplicated in handleBoolOperation 352 // method. 353 // FIXME: Once we model std::move for smart pointers clean up this and use 354 // that modeling. 355 handleBoolConversion(Call, C); 356 return true; 357 } else { 358 if (!move::isMovedFrom(State, ThisR)) { 359 // TODO: Model this case as well. At least, avoid invalidation of 360 // globals. 361 return false; 362 } 363 364 // TODO: Add a note to bug reports describing this decision. 365 C.addTransition(State->BindExpr( 366 Call.getOriginExpr(), C.getLocationContext(), 367 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 368 369 return true; 370 } 371 } 372 373 if (!ModelSmartPtrDereference) 374 return false; 375 376 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 377 if (CC->getDecl()->isCopyConstructor()) 378 return false; 379 380 const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 381 if (!ThisRegion) 382 return false; 383 384 QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); 385 386 if (CC->getDecl()->isMoveConstructor()) 387 return handleMoveCtr(Call, C, ThisRegion); 388 389 if (Call.getNumArgs() == 0) { 390 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 391 State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 392 393 C.addTransition( 394 State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 395 llvm::raw_ostream &OS) { 396 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 397 !BR.isInteresting(ThisRegion)) 398 return; 399 OS << "Default constructed smart pointer"; 400 checkAndPrettyPrintRegion(OS, ThisRegion); 401 OS << " is null"; 402 })); 403 } else { 404 const auto *TrackingExpr = Call.getArgExpr(0); 405 assert(TrackingExpr->getType()->isPointerType() && 406 "Adding a non pointer value to TrackedRegionMap"); 407 auto ArgVal = Call.getArgSVal(0); 408 State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 409 410 C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 411 ArgVal](PathSensitiveBugReport &BR, 412 llvm::raw_ostream &OS) { 413 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 414 !BR.isInteresting(ThisRegion)) 415 return; 416 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 417 OS << "Smart pointer"; 418 checkAndPrettyPrintRegion(OS, ThisRegion); 419 if (ArgVal.isZeroConstant()) 420 OS << " is constructed using a null value"; 421 else 422 OS << " is constructed"; 423 })); 424 } 425 return true; 426 } 427 428 if (handleAssignOp(Call, C)) 429 return true; 430 431 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 432 if (!Handler) 433 return false; 434 (this->**Handler)(Call, C); 435 436 return C.isDifferent(); 437 } 438 439 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( 440 ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E, 441 QualType Type, CheckerContext &C) const { 442 const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); 443 if (Ptr) 444 return {*Ptr, State}; 445 auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(), 446 Type, C.blockCount()); 447 State = State->set<TrackedRegionMap>(ThisRegion, Val); 448 return {Val, State}; 449 } 450 451 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, 452 CheckerContext &C) const { 453 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 454 if (!FC) 455 return false; 456 const FunctionDecl *FD = FC->getDecl(); 457 if (!FD->isOverloadedOperator()) 458 return false; 459 const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 460 if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || 461 OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || 462 OOK == OO_Spaceship)) 463 return false; 464 465 // There are some special cases about which we can infer about 466 // the resulting answer. 467 // For reference, there is a discussion at https://reviews.llvm.org/D104616. 468 // Also, the cppreference page is good to look at 469 // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. 470 471 auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, 472 SVal S) -> std::pair<SVal, ProgramStateRef> { 473 if (S.isZeroConstant()) { 474 return {S, State}; 475 } 476 const MemRegion *Reg = S.getAsRegion(); 477 assert(Reg && 478 "this pointer of std::unique_ptr should be obtainable as MemRegion"); 479 QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); 480 return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C); 481 }; 482 483 SVal First = Call.getArgSVal(0); 484 SVal Second = Call.getArgSVal(1); 485 const auto *FirstExpr = Call.getArgExpr(0); 486 const auto *SecondExpr = Call.getArgExpr(1); 487 488 const auto *ResultExpr = Call.getOriginExpr(); 489 const auto *LCtx = C.getLocationContext(); 490 auto &Bldr = C.getSValBuilder(); 491 ProgramStateRef State = C.getState(); 492 493 SVal FirstPtrVal, SecondPtrVal; 494 std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First); 495 std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, 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.getOriginExpr(), 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.getOriginExpr(), 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 const LocationContext *LC = C.getLocationContext(); 855 InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 856 CallExpr, LC, InnerPointerType, C.blockCount()); 857 State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 858 } 859 860 if (State->isNull(InnerPointerVal).isConstrainedTrue()) { 861 State = State->BindExpr(CallExpr, C.getLocationContext(), 862 C.getSValBuilder().makeTruthVal(false)); 863 864 C.addTransition(State); 865 return; 866 } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { 867 State = State->BindExpr(CallExpr, C.getLocationContext(), 868 C.getSValBuilder().makeTruthVal(true)); 869 870 C.addTransition(State); 871 return; 872 } else if (move::isMovedFrom(State, ThisRegion)) { 873 C.addTransition( 874 State->BindExpr(CallExpr, C.getLocationContext(), 875 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 876 return; 877 } else { 878 ProgramStateRef NotNullState, NullState; 879 std::tie(NotNullState, NullState) = 880 State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); 881 882 auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); 883 // Explicitly tracking the region as null. 884 NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); 885 886 NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), 887 C.getSValBuilder().makeTruthVal(false)); 888 C.addTransition(NullState, C.getNoteTag( 889 [ThisRegion](PathSensitiveBugReport &BR, 890 llvm::raw_ostream &OS) { 891 OS << "Assuming smart pointer"; 892 checkAndPrettyPrintRegion(OS, ThisRegion); 893 OS << " is null"; 894 }, 895 /*IsPrunable=*/true)); 896 NotNullState = 897 NotNullState->BindExpr(CallExpr, C.getLocationContext(), 898 C.getSValBuilder().makeTruthVal(true)); 899 C.addTransition( 900 NotNullState, 901 C.getNoteTag( 902 [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 903 OS << "Assuming smart pointer"; 904 checkAndPrettyPrintRegion(OS, ThisRegion); 905 OS << " is non-null"; 906 }, 907 /*IsPrunable=*/true)); 908 return; 909 } 910 } 911 912 void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 913 auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 914 Checker->ModelSmartPtrDereference = 915 Mgr.getAnalyzerOptions().getCheckerBooleanOption( 916 Checker, "ModelSmartPtrDereference"); 917 } 918 919 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 920 const LangOptions &LO = mgr.getLangOpts(); 921 return LO.CPlusPlus; 922 } 923