1 // SmartPtrChecker.cpp - Check for smart pointer dereference - 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 check for null dereference of C++ smart 10 // pointer. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "SmartPtr.h" 14 15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" 23 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 29 static const BugType *NullDereferenceBugTypePtr; 30 31 class SmartPtrChecker : public Checker<check::PreCall> { 32 public: 33 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 34 BugType NullDereferenceBugType{this, "Null SmartPtr dereference", 35 "C++ Smart Pointer"}; 36 37 private: 38 void reportBug(CheckerContext &C, const MemRegion *DerefRegion, 39 const CallEvent &Call) const; 40 void explainDereference(llvm::raw_ostream &OS, const MemRegion *DerefRegion, 41 const CallEvent &Call) const; 42 }; 43 } // end of anonymous namespace 44 45 // Define the inter-checker API. 46 namespace clang { 47 namespace ento { 48 namespace smartptr { 49 50 const BugType *getNullDereferenceBugType() { return NullDereferenceBugTypePtr; } 51 52 } // namespace smartptr 53 } // namespace ento 54 } // namespace clang 55 56 void SmartPtrChecker::checkPreCall(const CallEvent &Call, 57 CheckerContext &C) const { 58 if (!smartptr::isStdSmartPtrCall(Call)) 59 return; 60 ProgramStateRef State = C.getState(); 61 const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); 62 if (!OC) 63 return; 64 const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); 65 if (!ThisRegion) 66 return; 67 68 OverloadedOperatorKind OOK = OC->getOverloadedOperator(); 69 if (OOK == OO_Star || OOK == OO_Arrow) { 70 if (smartptr::isNullSmartPtr(State, ThisRegion)) 71 reportBug(C, ThisRegion, Call); 72 } 73 } 74 75 void SmartPtrChecker::reportBug(CheckerContext &C, const MemRegion *DerefRegion, 76 const CallEvent &Call) const { 77 ExplodedNode *ErrNode = C.generateErrorNode(); 78 if (!ErrNode) 79 return; 80 llvm::SmallString<128> Str; 81 llvm::raw_svector_ostream OS(Str); 82 explainDereference(OS, DerefRegion, Call); 83 auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType, 84 OS.str(), ErrNode); 85 R->markInteresting(DerefRegion); 86 C.emitReport(std::move(R)); 87 } 88 89 void SmartPtrChecker::explainDereference(llvm::raw_ostream &OS, 90 const MemRegion *DerefRegion, 91 const CallEvent &Call) const { 92 OS << "Dereference of null smart pointer "; 93 DerefRegion->printPretty(OS); 94 } 95 96 void ento::registerSmartPtrChecker(CheckerManager &Mgr) { 97 SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>(); 98 NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType; 99 } 100 101 bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) { 102 const LangOptions &LO = mgr.getLangOpts(); 103 return LO.CPlusPlus; 104 } 105