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