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 DefaultBool ModelSmartPtrDereference; 52 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 53 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 54 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 55 ProgramStateRef 56 checkRegionChanges(ProgramStateRef State, 57 const InvalidatedSymbols *Invalidated, 58 ArrayRef<const MemRegion *> ExplicitRegions, 59 ArrayRef<const MemRegion *> Regions, 60 const LocationContext *LCtx, const CallEvent *Call) const; 61 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, 62 const char *Sep) const override; 63 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 64 65 private: 66 void handleReset(const CallEvent &Call, CheckerContext &C) const; 67 void handleRelease(const CallEvent &Call, CheckerContext &C) const; 68 void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; 69 void handleGet(const CallEvent &Call, CheckerContext &C) const; 70 bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; 71 bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, 72 const MemRegion *ThisRegion) const; 73 bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, 74 const MemRegion *OtherSmartPtrRegion) 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 (Call.isCalled(StdSwapCall)) { 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 (Call.isCalled(StdMakeUniqueCall) || 299 Call.isCalled(StdMakeUniqueForOverwriteCall)) { 300 if (!ModelSmartPtrDereference) 301 return false; 302 303 const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); 304 if (!ThisRegionOpt) 305 return false; 306 307 const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal( 308 Call.getOriginExpr(), C.getLocationContext(), 309 getPointerTypeFromTemplateArg(Call, C), C.blockCount()); 310 311 const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion(); 312 State = State->set<TrackedRegionMap>(ThisRegion, PtrVal); 313 State = State->assume(PtrVal, true); 314 315 // TODO: ExprEngine should do this for us. 316 // For a bit more context: 317 // 1) Why do we need this? Since we are modelling a "function" 318 // that returns a constructed object we need to store this information in 319 // the program state. 320 // 321 // 2) Why does this work? 322 // `updateObjectsUnderConstruction` does exactly as it sounds. 323 // 324 // 3) How should it look like when moved to the Engine? 325 // It would be nice if we can just 326 // pretend we don't need to know about this - ie, completely automatic work. 327 // However, realistically speaking, I think we would need to "signal" the 328 // ExprEngine evalCall handler that we are constructing an object with this 329 // function call (constructors obviously construct, hence can be 330 // automatically deduced). 331 auto &Engine = State->getStateManager().getOwningEngine(); 332 State = Engine.updateObjectsUnderConstruction( 333 *ThisRegionOpt, nullptr, State, C.getLocationContext(), 334 Call.getConstructionContext(), {}); 335 336 // We don't leave a note here since it is guaranteed the 337 // unique_ptr from this call is non-null (hence is safe to de-reference). 338 C.addTransition(State); 339 return true; 340 } 341 342 if (!smartptr::isStdSmartPtrCall(Call)) 343 return false; 344 345 if (isBoolConversionMethod(Call)) { 346 const MemRegion *ThisR = 347 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 348 349 if (ModelSmartPtrDereference) { 350 // The check for the region is moved is duplicated in handleBoolOperation 351 // method. 352 // FIXME: Once we model std::move for smart pointers clean up this and use 353 // that modeling. 354 handleBoolConversion(Call, C); 355 return true; 356 } else { 357 if (!move::isMovedFrom(State, ThisR)) { 358 // TODO: Model this case as well. At least, avoid invalidation of 359 // globals. 360 return false; 361 } 362 363 // TODO: Add a note to bug reports describing this decision. 364 C.addTransition(State->BindExpr( 365 Call.getOriginExpr(), C.getLocationContext(), 366 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 367 368 return true; 369 } 370 } 371 372 if (!ModelSmartPtrDereference) 373 return false; 374 375 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 376 if (CC->getDecl()->isCopyConstructor()) 377 return false; 378 379 const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); 380 if (!ThisRegion) 381 return false; 382 383 if (CC->getDecl()->isMoveConstructor()) 384 return handleMoveCtr(Call, C, ThisRegion); 385 386 if (Call.getNumArgs() == 0) { 387 auto NullVal = C.getSValBuilder().makeNull(); 388 State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 389 390 C.addTransition( 391 State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 392 llvm::raw_ostream &OS) { 393 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 394 !BR.isInteresting(ThisRegion)) 395 return; 396 OS << "Default constructed smart pointer"; 397 checkAndPrettyPrintRegion(OS, ThisRegion); 398 OS << " is null"; 399 })); 400 } else { 401 const auto *TrackingExpr = Call.getArgExpr(0); 402 assert(TrackingExpr->getType()->isPointerType() && 403 "Adding a non pointer value to TrackedRegionMap"); 404 auto ArgVal = Call.getArgSVal(0); 405 State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); 406 407 C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, 408 ArgVal](PathSensitiveBugReport &BR, 409 llvm::raw_ostream &OS) { 410 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 411 !BR.isInteresting(ThisRegion)) 412 return; 413 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 414 OS << "Smart pointer"; 415 checkAndPrettyPrintRegion(OS, ThisRegion); 416 if (ArgVal.isZeroConstant()) 417 OS << " is constructed using a null value"; 418 else 419 OS << " is constructed"; 420 })); 421 } 422 return true; 423 } 424 425 if (handleAssignOp(Call, C)) 426 return true; 427 428 const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); 429 if (!Handler) 430 return false; 431 (this->**Handler)(Call, C); 432 433 return C.isDifferent(); 434 } 435 436 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal( 437 ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E, 438 QualType Type, CheckerContext &C) const { 439 const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion); 440 if (Ptr) 441 return {*Ptr, State}; 442 auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(), 443 Type, C.blockCount()); 444 State = State->set<TrackedRegionMap>(ThisRegion, Val); 445 return {Val, State}; 446 } 447 448 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call, 449 CheckerContext &C) const { 450 const auto *FC = dyn_cast<SimpleFunctionCall>(&Call); 451 if (!FC) 452 return false; 453 const FunctionDecl *FD = FC->getDecl(); 454 if (!FD->isOverloadedOperator()) 455 return false; 456 const OverloadedOperatorKind OOK = FD->getOverloadedOperator(); 457 if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less || 458 OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual || 459 OOK == OO_Spaceship)) 460 return false; 461 462 // There are some special cases about which we can infer about 463 // the resulting answer. 464 // For reference, there is a discussion at https://reviews.llvm.org/D104616. 465 // Also, the cppreference page is good to look at 466 // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp. 467 468 auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E, 469 SVal S) -> std::pair<SVal, ProgramStateRef> { 470 if (S.isZeroConstant()) { 471 return {S, State}; 472 } 473 const MemRegion *Reg = S.getAsRegion(); 474 assert(Reg && 475 "this pointer of std::unique_ptr should be obtainable as MemRegion"); 476 QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl()); 477 return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C); 478 }; 479 480 SVal First = Call.getArgSVal(0); 481 SVal Second = Call.getArgSVal(1); 482 const auto *FirstExpr = Call.getArgExpr(0); 483 const auto *SecondExpr = Call.getArgExpr(1); 484 485 const auto *ResultExpr = Call.getOriginExpr(); 486 const auto *LCtx = C.getLocationContext(); 487 auto &Bldr = C.getSValBuilder(); 488 ProgramStateRef State = C.getState(); 489 490 SVal FirstPtrVal, SecondPtrVal; 491 std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First); 492 std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second); 493 BinaryOperatorKind BOK = 494 operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe(); 495 auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal, 496 Call.getResultType()); 497 498 if (OOK != OO_Spaceship) { 499 ProgramStateRef TrueState, FalseState; 500 std::tie(TrueState, FalseState) = 501 State->assume(*RetVal.getAs<DefinedOrUnknownSVal>()); 502 if (TrueState) 503 C.addTransition( 504 TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true))); 505 if (FalseState) 506 C.addTransition( 507 FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false))); 508 } else { 509 C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal)); 510 } 511 return true; 512 } 513 514 bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call, 515 CheckerContext &C) const { 516 // operator<< does not modify the smart pointer. 517 // And we don't really have much of modelling of basic_ostream. 518 // So, we are better off: 519 // 1) Invalidating the mem-region of the ostream object at hand. 520 // 2) Setting the SVal of the basic_ostream as the return value. 521 // Not very satisfying, but it gets the job done, and is better 522 // than the default handling. :) 523 524 ProgramStateRef State = C.getState(); 525 const auto StreamVal = Call.getArgSVal(0); 526 const MemRegion *StreamThisRegion = StreamVal.getAsRegion(); 527 if (!StreamThisRegion) 528 return false; 529 State = 530 State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(), 531 C.blockCount(), C.getLocationContext(), false); 532 State = 533 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal); 534 C.addTransition(State); 535 return true; 536 } 537 538 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, 539 CheckerContext &C) const { 540 ProgramStateRef State = C.getState(); 541 // Clean up dead regions from the region map. 542 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 543 for (auto E : TrackedRegions) { 544 const MemRegion *Region = E.first; 545 bool IsRegDead = !SymReaper.isLiveRegion(Region); 546 547 if (IsRegDead) 548 State = State->remove<TrackedRegionMap>(Region); 549 } 550 C.addTransition(State); 551 } 552 553 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, 554 const char *NL, const char *Sep) const { 555 TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 556 557 if (!RS.isEmpty()) { 558 Out << Sep << "Smart ptr regions :" << NL; 559 for (auto I : RS) { 560 I.first->dumpToStream(Out); 561 if (smartptr::isNullSmartPtr(State, I.first)) 562 Out << ": Null"; 563 else 564 Out << ": Non Null"; 565 Out << NL; 566 } 567 } 568 } 569 570 ProgramStateRef SmartPtrModeling::checkRegionChanges( 571 ProgramStateRef State, const InvalidatedSymbols *Invalidated, 572 ArrayRef<const MemRegion *> ExplicitRegions, 573 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, 574 const CallEvent *Call) const { 575 TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); 576 TrackedRegionMapTy::Factory &RegionMapFactory = 577 State->get_context<TrackedRegionMap>(); 578 for (const auto *Region : Regions) 579 RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, 580 Region->getBaseRegion()); 581 return State->set<TrackedRegionMap>(RegionMap); 582 } 583 584 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, 585 SymbolReaper &SR) const { 586 // Marking tracked symbols alive 587 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 588 for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { 589 SVal Val = I->second; 590 for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { 591 SR.markLive(*si); 592 } 593 } 594 } 595 596 void SmartPtrModeling::handleReset(const CallEvent &Call, 597 CheckerContext &C) const { 598 ProgramStateRef State = C.getState(); 599 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 600 if (!IC) 601 return; 602 603 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 604 if (!ThisRegion) 605 return; 606 607 assert(Call.getArgExpr(0)->getType()->isPointerType() && 608 "Adding a non pointer value to TrackedRegionMap"); 609 State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); 610 const auto *TrackingExpr = Call.getArgExpr(0); 611 C.addTransition( 612 State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, 613 llvm::raw_ostream &OS) { 614 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 615 !BR.isInteresting(ThisRegion)) 616 return; 617 bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); 618 OS << "Smart pointer"; 619 checkAndPrettyPrintRegion(OS, ThisRegion); 620 OS << " reset using a null value"; 621 })); 622 // TODO: Make sure to ivalidate the region in the Store if we don't have 623 // time to model all methods. 624 } 625 626 void SmartPtrModeling::handleRelease(const CallEvent &Call, 627 CheckerContext &C) const { 628 ProgramStateRef State = C.getState(); 629 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 630 if (!IC) 631 return; 632 633 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 634 if (!ThisRegion) 635 return; 636 637 const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); 638 639 if (InnerPointVal) { 640 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 641 *InnerPointVal); 642 } 643 644 auto ValueToUpdate = C.getSValBuilder().makeNull(); 645 State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); 646 647 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 648 llvm::raw_ostream &OS) { 649 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 650 !BR.isInteresting(ThisRegion)) 651 return; 652 653 OS << "Smart pointer"; 654 checkAndPrettyPrintRegion(OS, ThisRegion); 655 OS << " is released and set to null"; 656 })); 657 // TODO: Add support to enable MallocChecker to start tracking the raw 658 // pointer. 659 } 660 661 void SmartPtrModeling::handleSwapMethod(const CallEvent &Call, 662 CheckerContext &C) const { 663 // To model unique_ptr::swap() method. 664 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 665 if (!IC) 666 return; 667 668 auto State = C.getState(); 669 handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C); 670 } 671 672 bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First, 673 SVal Second, CheckerContext &C) const { 674 const MemRegion *FirstThisRegion = First.getAsRegion(); 675 if (!FirstThisRegion) 676 return false; 677 const MemRegion *SecondThisRegion = Second.getAsRegion(); 678 if (!SecondThisRegion) 679 return false; 680 681 const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion); 682 const auto *SecondInnerPtrVal = 683 State->get<TrackedRegionMap>(SecondThisRegion); 684 685 State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal); 686 State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal); 687 688 C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion]( 689 PathSensitiveBugReport &BR, 690 llvm::raw_ostream &OS) { 691 if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 692 return; 693 if (BR.isInteresting(FirstThisRegion) && 694 !BR.isInteresting(SecondThisRegion)) { 695 BR.markInteresting(SecondThisRegion); 696 BR.markNotInteresting(FirstThisRegion); 697 } 698 if (BR.isInteresting(SecondThisRegion) && 699 !BR.isInteresting(FirstThisRegion)) { 700 BR.markInteresting(FirstThisRegion); 701 BR.markNotInteresting(SecondThisRegion); 702 } 703 // TODO: We need to emit some note here probably!! 704 })); 705 706 return true; 707 } 708 709 void SmartPtrModeling::handleGet(const CallEvent &Call, 710 CheckerContext &C) const { 711 ProgramStateRef State = C.getState(); 712 const auto *IC = dyn_cast<CXXInstanceCall>(&Call); 713 if (!IC) 714 return; 715 716 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 717 if (!ThisRegion) 718 return; 719 720 SVal InnerPointerVal; 721 std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal( 722 State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C); 723 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 724 InnerPointerVal); 725 // TODO: Add NoteTag, for how the raw pointer got using 'get' method. 726 C.addTransition(State); 727 } 728 729 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, 730 CheckerContext &C) const { 731 ProgramStateRef State = C.getState(); 732 const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); 733 if (!OC) 734 return false; 735 OverloadedOperatorKind OOK = OC->getOverloadedOperator(); 736 if (OOK != OO_Equal) 737 return false; 738 const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); 739 if (!ThisRegion) 740 return false; 741 742 const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); 743 // In case of 'nullptr' or '0' assigned 744 if (!OtherSmartPtrRegion) { 745 bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); 746 if (!AssignedNull) 747 return false; 748 auto NullVal = C.getSValBuilder().makeNull(); 749 State = State->set<TrackedRegionMap>(ThisRegion, NullVal); 750 C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, 751 llvm::raw_ostream &OS) { 752 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 753 !BR.isInteresting(ThisRegion)) 754 return; 755 OS << "Smart pointer"; 756 checkAndPrettyPrintRegion(OS, ThisRegion); 757 OS << " is assigned to null"; 758 })); 759 return true; 760 } 761 762 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 763 } 764 765 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, 766 const MemRegion *ThisRegion) const { 767 const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); 768 if (!OtherSmartPtrRegion) 769 return false; 770 771 return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); 772 } 773 774 bool SmartPtrModeling::updateMovedSmartPointers( 775 CheckerContext &C, const MemRegion *ThisRegion, 776 const MemRegion *OtherSmartPtrRegion) const { 777 ProgramStateRef State = C.getState(); 778 const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); 779 if (OtherInnerPtr) { 780 State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); 781 auto NullVal = C.getSValBuilder().makeNull(); 782 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 783 bool IsArgValNull = OtherInnerPtr->isZeroConstant(); 784 785 C.addTransition( 786 State, 787 C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( 788 PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 789 if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) 790 return; 791 if (BR.isInteresting(OtherSmartPtrRegion)) { 792 OS << "Smart pointer"; 793 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 794 OS << " is null after being moved to"; 795 checkAndPrettyPrintRegion(OS, ThisRegion); 796 } 797 if (BR.isInteresting(ThisRegion) && IsArgValNull) { 798 OS << "A null pointer value is moved to"; 799 checkAndPrettyPrintRegion(OS, ThisRegion); 800 BR.markInteresting(OtherSmartPtrRegion); 801 } 802 })); 803 return true; 804 } else { 805 // In case we dont know anything about value we are moving from 806 // remove the entry from map for which smart pointer got moved to. 807 auto NullVal = C.getSValBuilder().makeNull(); 808 State = State->remove<TrackedRegionMap>(ThisRegion); 809 State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); 810 C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, 811 ThisRegion](PathSensitiveBugReport &BR, 812 llvm::raw_ostream &OS) { 813 if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || 814 !BR.isInteresting(OtherSmartPtrRegion)) 815 return; 816 OS << "Smart pointer"; 817 checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); 818 OS << " is null after; previous value moved to"; 819 checkAndPrettyPrintRegion(OS, ThisRegion); 820 })); 821 return true; 822 } 823 return false; 824 } 825 826 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, 827 CheckerContext &C) const { 828 // To model unique_ptr::operator bool 829 ProgramStateRef State = C.getState(); 830 const Expr *CallExpr = Call.getOriginExpr(); 831 const MemRegion *ThisRegion = 832 cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); 833 834 SVal InnerPointerVal; 835 if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { 836 InnerPointerVal = *InnerValPtr; 837 } else { 838 // In case of inner pointer SVal is not available we create 839 // conjureSymbolVal for inner pointer value. 840 auto InnerPointerType = getInnerPointerType(Call, C); 841 if (InnerPointerType.isNull()) 842 return; 843 844 const LocationContext *LC = C.getLocationContext(); 845 InnerPointerVal = C.getSValBuilder().conjureSymbolVal( 846 CallExpr, LC, InnerPointerType, C.blockCount()); 847 State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); 848 } 849 850 if (State->isNull(InnerPointerVal).isConstrainedTrue()) { 851 State = State->BindExpr(CallExpr, C.getLocationContext(), 852 C.getSValBuilder().makeTruthVal(false)); 853 854 C.addTransition(State); 855 return; 856 } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { 857 State = State->BindExpr(CallExpr, C.getLocationContext(), 858 C.getSValBuilder().makeTruthVal(true)); 859 860 C.addTransition(State); 861 return; 862 } else if (move::isMovedFrom(State, ThisRegion)) { 863 C.addTransition( 864 State->BindExpr(CallExpr, C.getLocationContext(), 865 C.getSValBuilder().makeZeroVal(Call.getResultType()))); 866 return; 867 } else { 868 ProgramStateRef NotNullState, NullState; 869 std::tie(NotNullState, NullState) = 870 State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); 871 872 auto NullVal = C.getSValBuilder().makeNull(); 873 // Explicitly tracking the region as null. 874 NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); 875 876 NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), 877 C.getSValBuilder().makeTruthVal(false)); 878 C.addTransition(NullState, C.getNoteTag( 879 [ThisRegion](PathSensitiveBugReport &BR, 880 llvm::raw_ostream &OS) { 881 OS << "Assuming smart pointer"; 882 checkAndPrettyPrintRegion(OS, ThisRegion); 883 OS << " is null"; 884 }, 885 /*IsPrunable=*/true)); 886 NotNullState = 887 NotNullState->BindExpr(CallExpr, C.getLocationContext(), 888 C.getSValBuilder().makeTruthVal(true)); 889 C.addTransition( 890 NotNullState, 891 C.getNoteTag( 892 [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { 893 OS << "Assuming smart pointer"; 894 checkAndPrettyPrintRegion(OS, ThisRegion); 895 OS << " is non-null"; 896 }, 897 /*IsPrunable=*/true)); 898 return; 899 } 900 } 901 902 void ento::registerSmartPtrModeling(CheckerManager &Mgr) { 903 auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); 904 Checker->ModelSmartPtrDereference = 905 Mgr.getAnalyzerOptions().getCheckerBooleanOption( 906 Checker, "ModelSmartPtrDereference"); 907 } 908 909 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { 910 const LangOptions &LO = mgr.getLangOpts(); 911 return LO.CPlusPlus; 912 } 913