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