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/CallEvent.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 30 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 31 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" 32 #include <string> 33 34 using namespace clang; 35 using namespace ento; 36 37 namespace { 38 class SmartPtrModeling 39 : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, 40 check::LiveSymbols> { 41 42 bool isBoolConversionMethod(const CallEvent &Call) const; 43 44 public: 45 // Whether the checker should model for null dereferences of smart pointers. 46 DefaultBool ModelSmartPtrDereference; 47 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 48 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 49 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 50 ProgramStateRef 51 checkRegionChanges(ProgramStateRef State, 52 const InvalidatedSymbols *Invalidated, 53 ArrayRef<const MemRegion *> ExplicitRegions, 54 ArrayRef<const MemRegion *> Regions, 55 const LocationContext *LCtx, const CallEvent *Call) const; 56 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 57 const char *Sep) const override; 58 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 59 60 private: 61 void handleReset(const CallEvent &Call, CheckerContext &C) const; 62 void handleRelease(const CallEvent &Call, CheckerContext &C) const; 63 void handleSwap(const CallEvent &Call, CheckerContext &C) const; 64 void handleGet(const CallEvent &Call, CheckerContext &C) const; 65 bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; 66 bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, 67 const MemRegion *ThisRegion) const; 68 bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, 69 const MemRegion *OtherSmartPtrRegion) const; 70 void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; 71 72 using SmartPtrMethodHandlerFn = 73 void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; 74 CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ 75 {{"reset"}, &SmartPtrModeling::handleReset}, 76 {{"release"}, &SmartPtrModeling::handleRelease}, 77 {{"swap", 1}, &SmartPtrModeling::handleSwap}, 78 {{"get"}, &SmartPtrModeling::handleGet}}; 79 }; 80 } // end of anonymous namespace 81 82 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) 83 84 // Define the inter-checker API. 85 namespace clang { 86 namespace ento { 87 namespace smartptr { 88 bool isStdSmartPtrCall(const CallEvent &Call) { 89 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 90 if (!MethodDecl || !MethodDecl->getParent()) 91 return false; 92 93 const auto *RecordDecl = MethodDecl->getParent(); 94 if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) 95 return false; 96 97 if (RecordDecl->getDeclName().isIdentifier()) { 98 StringRef Name = RecordDecl->getName(); 99 return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; 100 } 101 return false; 102 } 103 104 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { 105 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 106 return InnerPointVal && 107 !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); 108 } 109 } // namespace smartptr 110 } // namespace ento 111 } // namespace clang 112 113 // If a region is removed all of the subregions need to be removed too. 114 static TrackedRegionMapTy 115 removeTrackedSubregions(TrackedRegionMapTy RegionMap, 116 TrackedRegionMapTy::Factory &RegionMapFactory, 117 const MemRegion *Region) { 118 if (!Region) 119 return RegionMap; 120 for (const auto &E : RegionMap) { 121 if (E.first->isSubRegionOf(Region)) 122 RegionMap = RegionMapFactory.remove(RegionMap, E.first); 123 } 124 return RegionMap; 125 } 126 127 static ProgramStateRef updateSwappedRegion(ProgramStateRef State, 128 const MemRegion *Region, 129 const SVal *RegionInnerPointerVal) { 130 if (RegionInnerPointerVal) { 131 State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); 132 } else { 133 State = State->remove<TrackedRegionMap>(Region); 134 } 135 return State; 136 } 137 138 // Helper method to get the inner pointer type of specialized smart pointer 139 // Returns empty type if not found valid inner pointer type. 140 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { 141 const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 142 if (!MethodDecl || !MethodDecl->getParent()) 143 return {}; 144 145 const auto *RecordDecl = MethodDecl->getParent(); 146 if (!RecordDecl || !RecordDecl->isInStdNamespace()) 147 return {}; 148 149 const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl); 150 if (!TSD) 151 return {}; 152 153 auto TemplateArgs = TSD->getTemplateArgs().asArray(); 154 if (TemplateArgs.size() == 0) 155 return {}; 156 auto InnerValueType = TemplateArgs[0].getAsType(); 157 return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); 158 } 159 160 // Helper method to pretty print region and avoid extra spacing. 161 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, 162 const MemRegion *Region) { 163 if (Region->canPrintPretty()) { 164 OS << " "; 165 Region->printPretty(OS); 166 } 167 } 168 169 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { 170 // TODO: Update CallDescription to support anonymous calls? 171 // TODO: Handle other methods, such as .get() or .release(). 172 // But once we do, we'd need a visitor to explain null dereferences 173 // that are found via such modeling. 174 const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); 175 return CD && CD->getConversionType()->isBooleanType(); 176 } 177 178 bool SmartPtrModeling::evalCall(const CallEvent &Call, 179 CheckerContext &C) const { 180 ProgramStateRef State = C.getState(); 181 if (!smartptr::isStdSmartPtrCall(Call)) 182 return false; 183 184 if (isBoolConversionMethod(Call)) { 185 const MemRegion *ThisR = 186 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 187 188 if (ModelSmartPtrDereference) { 189 // The check for the region is moved is duplicated in handleBoolOperation 190 // method. 191 // FIXME: Once we model std::move for smart pointers clean up this and use 192 // that modeling. 193 handleBoolConversion(Call, C); 194 return true; 195 } else { 196 if (!move::isMovedFrom(State, ThisR)) { 197 // TODO: Model this case as well. At least, avoid invalidation of 198 // globals. 199 return false; 200 } 201 202 // TODO: Add a note to bug reports describing this decision. 203 C.addTransition(State->BindExpr( 204 Call.getOriginExpr(), C.getLocationContext(), 205 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 206 207 return true; 208 } 209 } 210 211 if (!ModelSmartPtrDereference) 212 return false; 213 214 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 215 if (CC->getDecl()->isCopyConstructor()) 216 return false; 217 218 const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 219 if (!ThisRegion) 220 return false; 221 222 if (CC->getDecl()->isMoveConstructor()) 223 return handleMoveCtr(Call, C, ThisRegion); 224 225 if (Call.getNumArgs() == 0) { 226 auto NullVal = C.getSValBuilder().makeNull(); 227 State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 228 229 C.addTransition( 230 State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 231 llvm::raw_ostream &OS) { 232 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 233 !BR.isInteresting(ThisRegion)) 234 return; 235 OS << "Default constructed smart pointer"; 236 checkAndPrettyPrintRegion(OS, ThisRegion); 237 OS << " is null"; 238 })); 239 } else { 240 const auto *TrackingExpr = Call.getArgExpr(0); 241 assert(TrackingExpr->getType()->isPointerType() && 242 "Adding a non pointer value to TrackedRegionMap"); 243 auto ArgVal = Call.getArgSVal(0); 244 State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 245 246 C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 247 ArgVal](PathSensitiveBugReport &BR, 248 llvm::raw_ostream &OS) { 249 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 250 !BR.isInteresting(ThisRegion)) 251 return; 252 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 253 OS << "Smart pointer"; 254 checkAndPrettyPrintRegion(OS, ThisRegion); 255 if (ArgVal.isZeroConstant()) 256 OS << " is constructed using a null value"; 257 else 258 OS << " is constructed"; 259 })); 260 } 261 return true; 262 } 263 264 if (handleAssignOp(Call, C)) 265 return true; 266 267 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 268 if (!Handler) 269 return false; 270 (this->**Handler)(Call, C); 271 272 return C.isDifferent(); 273 } 274 275 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 276 CheckerContext &C) const { 277 ProgramStateRef State = C.getState(); 278 // Clean up dead regions from the region map. 279 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 280 for (auto E : TrackedRegions) { 281 const MemRegion *Region = E.first; 282 bool IsRegDead = !SymReaper.isLiveRegion(Region); 283 284 if (IsRegDead) 285 State = State->remove<TrackedRegionMap>(Region); 286 } 287 C.addTransition(State); 288 } 289 290 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, 291 const char *NL, const char *Sep) const { 292 TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 293 294 if (!RS.isEmpty()) { 295 Out << Sep << "Smart ptr regions :" << NL; 296 for (auto I : RS) { 297 I.first->dumpToStream(Out); 298 if (smartptr::isNullSmartPtr(State, I.first)) 299 Out << ": Null"; 300 else 301 Out << ": Non Null"; 302 Out << NL; 303 } 304 } 305 } 306 307 ProgramStateRef SmartPtrModeling::checkRegionChanges( 308 ProgramStateRef State, const InvalidatedSymbols *Invalidated, 309 ArrayRef<const MemRegion *> ExplicitRegions, 310 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 311 const CallEvent *Call) const { 312 TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); 313 TrackedRegionMapTy::Factory &RegionMapFactory = 314 State->get_context<TrackedRegionMap>(); 315 for (const auto *Region : Regions) 316 RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, 317 Region->getBaseRegion()); 318 return State->set<TrackedRegionMap>(RegionMap); 319 } 320 321 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, 322 SymbolReaper &SR) const { 323 // Marking tracked symbols alive 324 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 325 for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { 326 SVal Val = I->second; 327 for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { 328 SR.markLive(*si); 329 } 330 } 331 } 332 333 void SmartPtrModeling::handleReset(const CallEvent &Call, 334 CheckerContext &C) const { 335 ProgramStateRef State = C.getState(); 336 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 337 if (!IC) 338 return; 339 340 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 341 if (!ThisRegion) 342 return; 343 344 assert(Call.getArgExpr(0)->getType()->isPointerType() && 345 "Adding a non pointer value to TrackedRegionMap"); 346 State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); 347 const auto *TrackingExpr = Call.getArgExpr(0); 348 C.addTransition( 349 State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, 350 llvm::raw_ostream &OS) { 351 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 352 !BR.isInteresting(ThisRegion)) 353 return; 354 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 355 OS << "Smart pointer"; 356 checkAndPrettyPrintRegion(OS, ThisRegion); 357 OS << " reset using a null value"; 358 })); 359 // TODO: Make sure to ivalidate the region in the Store if we don't have 360 // time to model all methods. 361 } 362 363 void SmartPtrModeling::handleRelease(const CallEvent &Call, 364 CheckerContext &C) const { 365 ProgramStateRef State = C.getState(); 366 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 367 if (!IC) 368 return; 369 370 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 371 if (!ThisRegion) 372 return; 373 374 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 375 376 if (InnerPointVal) { 377 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 378 *InnerPointVal); 379 } 380 381 auto ValueToUpdate = C.getSValBuilder().makeNull(); 382 State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); 383 384 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 385 llvm::raw_ostream &OS) { 386 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 387 !BR.isInteresting(ThisRegion)) 388 return; 389 390 OS << "Smart pointer"; 391 checkAndPrettyPrintRegion(OS, ThisRegion); 392 OS << " is released and set to null"; 393 })); 394 // TODO: Add support to enable MallocChecker to start tracking the raw 395 // pointer. 396 } 397 398 void SmartPtrModeling::handleSwap(const CallEvent &Call, 399 CheckerContext &C) const { 400 // To model unique_ptr::swap() method. 401 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 402 if (!IC) 403 return; 404 405 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 406 if (!ThisRegion) 407 return; 408 409 const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); 410 if (!ArgRegion) 411 return; 412 413 auto State = C.getState(); 414 const auto *ThisRegionInnerPointerVal = 415 State->get<TrackedRegionMap>(ThisRegion); 416 const auto *ArgRegionInnerPointerVal = 417 State->get<TrackedRegionMap>(ArgRegion); 418 419 // Swap the tracked region values. 420 State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); 421 State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); 422 423 C.addTransition( 424 State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR, 425 llvm::raw_ostream &OS) { 426 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 427 !BR.isInteresting(ThisRegion)) 428 return; 429 BR.markInteresting(ArgRegion); 430 OS << "Swapped null smart pointer"; 431 checkAndPrettyPrintRegion(OS, ArgRegion); 432 OS << " with smart pointer"; 433 checkAndPrettyPrintRegion(OS, ThisRegion); 434 })); 435 } 436 437 void SmartPtrModeling::handleGet(const CallEvent &Call, 438 CheckerContext &C) const { 439 ProgramStateRef State = C.getState(); 440 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 441 if (!IC) 442 return; 443 444 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 445 if (!ThisRegion) 446 return; 447 448 SVal InnerPointerVal; 449 if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { 450 InnerPointerVal = *InnerValPtr; 451 } else { 452 const auto *CallExpr = Call.getOriginExpr(); 453 InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 454 CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount()); 455 State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 456 } 457 458 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 459 InnerPointerVal); 460 // TODO: Add NoteTag, for how the raw pointer got using 'get' method. 461 C.addTransition(State); 462 } 463 464 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, 465 CheckerContext &C) const { 466 ProgramStateRef State = C.getState(); 467 const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); 468 if (!OC) 469 return false; 470 OverloadedOperatorKind OOK = OC->getOverloadedOperator(); 471 if (OOK != OO_Equal) 472 return false; 473 const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); 474 if (!ThisRegion) 475 return false; 476 477 const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); 478 // In case of 'nullptr' or '0' assigned 479 if (!OtherSmartPtrRegion) { 480 bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); 481 if (!AssignedNull) 482 return false; 483 auto NullVal = C.getSValBuilder().makeNull(); 484 State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 485 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 486 llvm::raw_ostream &OS) { 487 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 488 !BR.isInteresting(ThisRegion)) 489 return; 490 OS << "Smart pointer"; 491 checkAndPrettyPrintRegion(OS, ThisRegion); 492 OS << " is assigned to null"; 493 })); 494 return true; 495 } 496 497 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 498 } 499 500 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, 501 const MemRegion *ThisRegion) const { 502 const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); 503 if (!OtherSmartPtrRegion) 504 return false; 505 506 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 507 } 508 509 bool SmartPtrModeling::updateMovedSmartPointers( 510 CheckerContext &C, const MemRegion *ThisRegion, 511 const MemRegion *OtherSmartPtrRegion) const { 512 ProgramStateRef State = C.getState(); 513 const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); 514 if (OtherInnerPtr) { 515 State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); 516 auto NullVal = C.getSValBuilder().makeNull(); 517 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 518 bool IsArgValNull = OtherInnerPtr->isZeroConstant(); 519 520 C.addTransition( 521 State, 522 C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( 523 PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 524 if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 525 return; 526 if (BR.isInteresting(OtherSmartPtrRegion)) { 527 OS << "Smart pointer"; 528 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 529 OS << " is null after being moved to"; 530 checkAndPrettyPrintRegion(OS, ThisRegion); 531 } 532 if (BR.isInteresting(ThisRegion) && IsArgValNull) { 533 OS << "A null pointer value is moved to"; 534 checkAndPrettyPrintRegion(OS, ThisRegion); 535 BR.markInteresting(OtherSmartPtrRegion); 536 } 537 })); 538 return true; 539 } else { 540 // In case we dont know anything about value we are moving from 541 // remove the entry from map for which smart pointer got moved to. 542 auto NullVal = C.getSValBuilder().makeNull(); 543 State = State->remove<TrackedRegionMap>(ThisRegion); 544 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 545 C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, 546 ThisRegion](PathSensitiveBugReport &BR, 547 llvm::raw_ostream &OS) { 548 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 549 !BR.isInteresting(OtherSmartPtrRegion)) 550 return; 551 OS << "Smart pointer"; 552 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 553 OS << " is null after; previous value moved to"; 554 checkAndPrettyPrintRegion(OS, ThisRegion); 555 })); 556 return true; 557 } 558 return false; 559 } 560 561 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, 562 CheckerContext &C) const { 563 // To model unique_ptr::operator bool 564 ProgramStateRef State = C.getState(); 565 const Expr *CallExpr = Call.getOriginExpr(); 566 const MemRegion *ThisRegion = 567 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 568 569 SVal InnerPointerVal; 570 if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { 571 InnerPointerVal = *InnerValPtr; 572 } else { 573 // In case of inner pointer SVal is not available we create 574 // conjureSymbolVal for inner pointer value. 575 auto InnerPointerType = getInnerPointerType(Call, C); 576 if (InnerPointerType.isNull()) 577 return; 578 579 const LocationContext *LC = C.getLocationContext(); 580 InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 581 CallExpr, LC, InnerPointerType, C.blockCount()); 582 State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 583 } 584 585 if (State->isNull(InnerPointerVal).isConstrainedTrue()) { 586 State = State->BindExpr(CallExpr, C.getLocationContext(), 587 C.getSValBuilder().makeTruthVal(false)); 588 589 C.addTransition(State); 590 return; 591 } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { 592 State = State->BindExpr(CallExpr, C.getLocationContext(), 593 C.getSValBuilder().makeTruthVal(true)); 594 595 C.addTransition(State); 596 return; 597 } else if (move::isMovedFrom(State, ThisRegion)) { 598 C.addTransition( 599 State->BindExpr(CallExpr, C.getLocationContext(), 600 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 601 return; 602 } else { 603 ProgramStateRef NotNullState, NullState; 604 std::tie(NotNullState, NullState) = 605 State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); 606 607 auto NullVal = C.getSValBuilder().makeNull(); 608 // Explicitly tracking the region as null. 609 NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); 610 611 NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), 612 C.getSValBuilder().makeTruthVal(false)); 613 C.addTransition(NullState, C.getNoteTag( 614 [ThisRegion](PathSensitiveBugReport &BR, 615 llvm::raw_ostream &OS) { 616 OS << "Assuming smart pointer"; 617 checkAndPrettyPrintRegion(OS, ThisRegion); 618 OS << " is null"; 619 }, 620 /*IsPrunable=*/true)); 621 NotNullState = 622 NotNullState->BindExpr(CallExpr, C.getLocationContext(), 623 C.getSValBuilder().makeTruthVal(true)); 624 C.addTransition( 625 NotNullState, 626 C.getNoteTag( 627 [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 628 OS << "Assuming smart pointer"; 629 checkAndPrettyPrintRegion(OS, ThisRegion); 630 OS << " is non-null"; 631 }, 632 /*IsPrunable=*/true)); 633 return; 634 } 635 } 636 637 void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 638 auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 639 Checker->ModelSmartPtrDereference = 640 Mgr.getAnalyzerOptions().getCheckerBooleanOption( 641 Checker, "ModelSmartPtrDereference"); 642 } 643 644 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 645 const LangOptions &LO = mgr.getLangOpts(); 646 return LO.CPlusPlus; 647 } 648