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