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