235 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the following new checkers for C++ delete expressions:10//11// * DeleteWithNonVirtualDtorChecker12// Defines a checker for the OOP52-CPP CERT rule: Do not delete a13// polymorphic object without a virtual destructor.14//15// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor16// report if an object with a virtual function but a non-virtual17// destructor exists or is deleted, respectively.18//19// This check exceeds them by comparing the dynamic and static types of20// the object at the point of destruction and only warns if it happens21// through a pointer to a base type without a virtual destructor. The22// check places a note at the last point where the conversion from23// derived to base happened.24//25// * CXXArrayDeleteChecker26// Defines a checker for the EXP51-CPP CERT rule: Do not delete an array27// 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 38using namespace clang;39using namespace ento;40 41namespace {42class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> {43protected: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 void56 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,57 const TypedValueRegion *BaseClassRegion,58 const SymbolicRegion *DerivedClassRegion) const = 0;59 60public:61 void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;62};63 64class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker {65 const BugType BT{66 this, "Destruction of a polymorphic object with no virtual destructor"};67 68 void69 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,70 const TypedValueRegion *BaseClassRegion,71 const SymbolicRegion *DerivedClassRegion) const override;72};73 74class CXXArrayDeleteChecker : public CXXDeleteChecker {75 const BugType BT{this,76 "Deleting an array of polymorphic objects is undefined"};77 78 void79 checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,80 const TypedValueRegion *BaseClassRegion,81 const SymbolicRegion *DerivedClassRegion) const override;82};83} // namespace84 85void 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 106void 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 136void 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 179PathDiagnosticPieceRef180CXXDeleteChecker::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 219void ento::registerArrayDeleteChecker(CheckerManager &mgr) {220 mgr.registerChecker<CXXArrayDeleteChecker>();221}222 223bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) {224 return true;225}226 227void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {228 mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();229}230 231bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(232 const CheckerManager &mgr) {233 return true;234}235