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