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