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