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