1 //===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- 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 // Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic 10 // object without a virtual destructor. 11 // 12 // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if 13 // an object with a virtual function but a non-virtual destructor exists or is 14 // deleted, respectively. 15 // 16 // This check exceeds them by comparing the dynamic and static types of the 17 // object at the point of destruction and only warns if it happens through a 18 // pointer to a base type without a virtual destructor. The check places a note 19 // at the last point where the conversion from derived to base happened. 20 // 21 //===----------------------------------------------------------------------===// 22 23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 24 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 26 #include "clang/StaticAnalyzer/Core/Checker.h" 27 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 30 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 31 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 32 33 using namespace clang; 34 using namespace ento; 35 36 namespace { 37 class DeleteWithNonVirtualDtorChecker 38 : public Checker<check::PreStmt<CXXDeleteExpr>> { 39 mutable std::unique_ptr<BugType> BT; 40 41 class DeleteBugVisitor : public BugReporterVisitor { 42 public: 43 DeleteBugVisitor() : Satisfied(false) {} 44 void Profile(llvm::FoldingSetNodeID &ID) const override { 45 static int X = 0; 46 ID.AddPointer(&X); 47 } 48 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 49 BugReporterContext &BRC, 50 PathSensitiveBugReport &BR) override; 51 52 private: 53 bool Satisfied; 54 }; 55 56 public: 57 void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; 58 }; 59 } // end anonymous namespace 60 61 void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, 62 CheckerContext &C) const { 63 const Expr *DeletedObj = DE->getArgument(); 64 const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); 65 if (!MR) 66 return; 67 68 const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); 69 const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); 70 if (!BaseClassRegion || !DerivedClassRegion) 71 return; 72 73 const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 74 const auto *DerivedClass = 75 DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 76 if (!BaseClass || !DerivedClass) 77 return; 78 79 if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 80 return; 81 82 if (BaseClass->getDestructor()->isVirtual()) 83 return; 84 85 if (!DerivedClass->isDerivedFrom(BaseClass)) 86 return; 87 88 if (!BT) 89 BT.reset(new BugType(this, 90 "Destruction of a polymorphic object with no " 91 "virtual destructor", 92 "Logic error")); 93 94 ExplodedNode *N = C.generateNonFatalErrorNode(); 95 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); 96 97 // Mark region of problematic base class for later use in the BugVisitor. 98 R->markInteresting(BaseClassRegion); 99 R->addVisitor(std::make_unique<DeleteBugVisitor>()); 100 C.emitReport(std::move(R)); 101 } 102 103 PathDiagnosticPieceRef 104 DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( 105 const ExplodedNode *N, BugReporterContext &BRC, 106 PathSensitiveBugReport &BR) { 107 // Stop traversal after the first conversion was found on a path. 108 if (Satisfied) 109 return nullptr; 110 111 const Stmt *S = N->getStmtForDiagnostics(); 112 if (!S) 113 return nullptr; 114 115 const auto *CastE = dyn_cast<CastExpr>(S); 116 if (!CastE) 117 return nullptr; 118 119 // Only interested in DerivedToBase implicit casts. 120 // Explicit casts can have different CastKinds. 121 if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { 122 if (ImplCastE->getCastKind() != CK_DerivedToBase) 123 return nullptr; 124 } 125 126 // Region associated with the current cast expression. 127 const MemRegion *M = N->getSVal(CastE).getAsRegion(); 128 if (!M) 129 return nullptr; 130 131 // Check if target region was marked as problematic previously. 132 if (!BR.isInteresting(M)) 133 return nullptr; 134 135 // Stop traversal on this path. 136 Satisfied = true; 137 138 SmallString<256> Buf; 139 llvm::raw_svector_ostream OS(Buf); 140 OS << "Conversion from derived to base happened here"; 141 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 142 N->getLocationContext()); 143 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 144 } 145 146 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { 147 mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); 148 } 149 150 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( 151 const LangOptions &LO) { 152 return true; 153 } 154