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