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 if (!N) 96 return; 97 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); 98 99 // Mark region of problematic base class for later use in the BugVisitor. 100 R->markInteresting(BaseClassRegion); 101 R->addVisitor(std::make_unique<DeleteBugVisitor>()); 102 C.emitReport(std::move(R)); 103 } 104 105 PathDiagnosticPieceRef 106 DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( 107 const ExplodedNode *N, BugReporterContext &BRC, 108 PathSensitiveBugReport &BR) { 109 // Stop traversal after the first conversion was found on a path. 110 if (Satisfied) 111 return nullptr; 112 113 const Stmt *S = N->getStmtForDiagnostics(); 114 if (!S) 115 return nullptr; 116 117 const auto *CastE = dyn_cast<CastExpr>(S); 118 if (!CastE) 119 return nullptr; 120 121 // Only interested in DerivedToBase implicit casts. 122 // Explicit casts can have different CastKinds. 123 if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { 124 if (ImplCastE->getCastKind() != CK_DerivedToBase) 125 return nullptr; 126 } 127 128 // Region associated with the current cast expression. 129 const MemRegion *M = N->getSVal(CastE).getAsRegion(); 130 if (!M) 131 return nullptr; 132 133 // Check if target region was marked as problematic previously. 134 if (!BR.isInteresting(M)) 135 return nullptr; 136 137 // Stop traversal on this path. 138 Satisfied = true; 139 140 SmallString<256> Buf; 141 llvm::raw_svector_ostream OS(Buf); 142 OS << "Conversion from derived to base happened here"; 143 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 144 N->getLocationContext()); 145 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 146 } 147 148 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { 149 mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); 150 } 151 152 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( 153 const CheckerManager &mgr) { 154 return true; 155 } 156