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