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 "ClangSACheckers.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 BugReporterVisitorImpl<DeleteBugVisitor> { 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 const ExplodedNode *PrevN, 51 BugReporterContext &BRC, 52 BugReport &BR) override; 53 54 private: 55 bool Satisfied; 56 }; 57 58 public: 59 void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; 60 }; 61 } // end anonymous namespace 62 63 void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, 64 CheckerContext &C) const { 65 const Expr *DeletedObj = DE->getArgument(); 66 const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); 67 if (!MR) 68 return; 69 70 const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); 71 const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); 72 if (!BaseClassRegion || !DerivedClassRegion) 73 return; 74 75 const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 76 const auto *DerivedClass = 77 DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 78 if (!BaseClass || !DerivedClass) 79 return; 80 81 if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 82 return; 83 84 if (BaseClass->getDestructor()->isVirtual()) 85 return; 86 87 if (!DerivedClass->isDerivedFrom(BaseClass)) 88 return; 89 90 if (!BT) 91 BT.reset(new BugType(this, 92 "Destruction of a polymorphic object with no " 93 "virtual destructor", 94 "Logic error")); 95 96 ExplodedNode *N = C.generateNonFatalErrorNode(); 97 auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); 98 99 // Mark region of problematic base class for later use in the BugVisitor. 100 R->markInteresting(BaseClassRegion); 101 R->addVisitor(llvm::make_unique<DeleteBugVisitor>()); 102 C.emitReport(std::move(R)); 103 } 104 105 std::shared_ptr<PathDiagnosticPiece> 106 DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( 107 const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, 108 BugReport &BR) { 109 // Stop traversal after the first conversion was found on a path. 110 if (Satisfied) 111 return nullptr; 112 113 const Stmt *S = PathDiagnosticLocation::getStmt(N); 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 nullptr); 147 } 148 149 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { 150 mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); 151 } 152