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