1 //=== CXXDeleteChecker.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 // This file defines the following new checkers for C++ delete expressions: 10 // 11 // * DeleteWithNonVirtualDtorChecker 12 // Defines a checker for the OOP52-CPP CERT rule: Do not delete a 13 // polymorphic object without a virtual destructor. 14 // 15 // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor 16 // report if an object with a virtual function but a non-virtual 17 // destructor exists or is deleted, respectively. 18 // 19 // This check exceeds them by comparing the dynamic and static types of 20 // the object at the point of destruction and only warns if it happens 21 // through a pointer to a base type without a virtual destructor. The 22 // check places a note at the last point where the conversion from 23 // derived to base happened. 24 // 25 // * CXXArrayDeleteChecker 26 // Defines a checker for the EXP51-CPP CERT rule: Do not delete an array 27 // through a pointer of the incorrect type. 28 // 29 //===----------------------------------------------------------------------===// 30 31 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 32 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 33 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 34 #include "clang/StaticAnalyzer/Core/Checker.h" 35 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 36 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 37 38 using namespace clang; 39 using namespace ento; 40 41 namespace { 42 class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> { 43 protected: 44 class PtrCastVisitor : public BugReporterVisitor { 45 public: 46 void Profile(llvm::FoldingSetNodeID &ID) const override { 47 static int X = 0; 48 ID.AddPointer(&X); 49 } 50 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 51 BugReporterContext &BRC, 52 PathSensitiveBugReport &BR) override; 53 }; 54 55 virtual void 56 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 57 const TypedValueRegion *BaseClassRegion, 58 const SymbolicRegion *DerivedClassRegion) const = 0; 59 60 public: 61 void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; 62 }; 63 64 class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker { 65 const BugType BT{ 66 this, "Destruction of a polymorphic object with no virtual destructor"}; 67 68 void 69 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 70 const TypedValueRegion *BaseClassRegion, 71 const SymbolicRegion *DerivedClassRegion) const override; 72 }; 73 74 class CXXArrayDeleteChecker : public CXXDeleteChecker { 75 const BugType BT{this, 76 "Deleting an array of polymorphic objects is undefined"}; 77 78 void 79 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 80 const TypedValueRegion *BaseClassRegion, 81 const SymbolicRegion *DerivedClassRegion) const override; 82 }; 83 } // namespace 84 85 void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE, 86 CheckerContext &C) const { 87 const Expr *DeletedObj = DE->getArgument(); 88 const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); 89 if (!MR) 90 return; 91 92 OverloadedOperatorKind DeleteKind = 93 DE->getOperatorDelete()->getOverloadedOperator(); 94 95 if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete) 96 return; 97 98 const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); 99 const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); 100 if (!BaseClassRegion || !DerivedClassRegion) 101 return; 102 103 checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion); 104 } 105 106 void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr( 107 const CXXDeleteExpr *DE, CheckerContext &C, 108 const TypedValueRegion *BaseClassRegion, 109 const SymbolicRegion *DerivedClassRegion) const { 110 const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 111 const auto *DerivedClass = 112 DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 113 if (!BaseClass || !DerivedClass) 114 return; 115 116 if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 117 return; 118 119 if (BaseClass->getDestructor()->isVirtual()) 120 return; 121 122 if (!DerivedClass->isDerivedFrom(BaseClass)) 123 return; 124 125 ExplodedNode *N = C.generateNonFatalErrorNode(); 126 if (!N) 127 return; 128 auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); 129 130 // Mark region of problematic base class for later use in the BugVisitor. 131 R->markInteresting(BaseClassRegion); 132 R->addVisitor<PtrCastVisitor>(); 133 C.emitReport(std::move(R)); 134 } 135 136 void CXXArrayDeleteChecker::checkTypedDeleteExpr( 137 const CXXDeleteExpr *DE, CheckerContext &C, 138 const TypedValueRegion *BaseClassRegion, 139 const SymbolicRegion *DerivedClassRegion) const { 140 const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 141 const auto *DerivedClass = 142 DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 143 if (!BaseClass || !DerivedClass) 144 return; 145 146 if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 147 return; 148 149 if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete) 150 return; 151 152 if (!DerivedClass->isDerivedFrom(BaseClass)) 153 return; 154 155 ExplodedNode *N = C.generateNonFatalErrorNode(); 156 if (!N) 157 return; 158 159 SmallString<256> Buf; 160 llvm::raw_svector_ostream OS(Buf); 161 162 QualType SourceType = BaseClassRegion->getValueType(); 163 QualType TargetType = 164 DerivedClassRegion->getSymbol()->getType()->getPointeeType(); 165 166 OS << "Deleting an array of '" << TargetType.getAsString() 167 << "' objects as their base class '" 168 << SourceType.getAsString(C.getASTContext().getPrintingPolicy()) 169 << "' is undefined"; 170 171 auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); 172 173 // Mark region of problematic base class for later use in the BugVisitor. 174 R->markInteresting(BaseClassRegion); 175 R->addVisitor<PtrCastVisitor>(); 176 C.emitReport(std::move(R)); 177 } 178 179 PathDiagnosticPieceRef 180 CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N, 181 BugReporterContext &BRC, 182 PathSensitiveBugReport &BR) { 183 const Stmt *S = N->getStmtForDiagnostics(); 184 if (!S) 185 return nullptr; 186 187 const auto *CastE = dyn_cast<CastExpr>(S); 188 if (!CastE) 189 return nullptr; 190 191 // FIXME: This way of getting base types does not support reference types. 192 QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType(); 193 QualType TargetType = CastE->getType()->getPointeeType(); 194 195 if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType) 196 return nullptr; 197 198 // Region associated with the current cast expression. 199 const MemRegion *M = N->getSVal(CastE).getAsRegion(); 200 if (!M) 201 return nullptr; 202 203 // Check if target region was marked as problematic previously. 204 if (!BR.isInteresting(M)) 205 return nullptr; 206 207 SmallString<256> Buf; 208 llvm::raw_svector_ostream OS(Buf); 209 210 OS << "Casting from '" << SourceType.getAsString() << "' to '" 211 << TargetType.getAsString() << "' here"; 212 213 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 214 N->getLocationContext()); 215 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), 216 /*addPosRange=*/true); 217 } 218 219 void ento::registerArrayDeleteChecker(CheckerManager &mgr) { 220 mgr.registerChecker<CXXArrayDeleteChecker>(); 221 } 222 223 bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) { 224 return true; 225 } 226 227 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { 228 mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); 229 } 230 231 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( 232 const CheckerManager &mgr) { 233 return true; 234 } 235