15f757f3fSDimitry Andric //=== CXXDeleteChecker.cpp -------------------------------------*- C++ -*--===// 25f757f3fSDimitry Andric // 35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f757f3fSDimitry Andric // 75f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 85f757f3fSDimitry Andric // 95f757f3fSDimitry Andric // This file defines the following new checkers for C++ delete expressions: 105f757f3fSDimitry Andric // 115f757f3fSDimitry Andric // * DeleteWithNonVirtualDtorChecker 125f757f3fSDimitry Andric // Defines a checker for the OOP52-CPP CERT rule: Do not delete a 135f757f3fSDimitry Andric // polymorphic object without a virtual destructor. 145f757f3fSDimitry Andric // 155f757f3fSDimitry Andric // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor 165f757f3fSDimitry Andric // report if an object with a virtual function but a non-virtual 175f757f3fSDimitry Andric // destructor exists or is deleted, respectively. 185f757f3fSDimitry Andric // 195f757f3fSDimitry Andric // This check exceeds them by comparing the dynamic and static types of 205f757f3fSDimitry Andric // the object at the point of destruction and only warns if it happens 215f757f3fSDimitry Andric // through a pointer to a base type without a virtual destructor. The 225f757f3fSDimitry Andric // check places a note at the last point where the conversion from 235f757f3fSDimitry Andric // derived to base happened. 245f757f3fSDimitry Andric // 255f757f3fSDimitry Andric // * CXXArrayDeleteChecker 265f757f3fSDimitry Andric // Defines a checker for the EXP51-CPP CERT rule: Do not delete an array 275f757f3fSDimitry Andric // through a pointer of the incorrect type. 285f757f3fSDimitry Andric // 295f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 305f757f3fSDimitry Andric 315f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 325f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 335f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 34647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" 355f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 365f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 375f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 385f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 395f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" 405f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 415f757f3fSDimitry Andric 425f757f3fSDimitry Andric using namespace clang; 435f757f3fSDimitry Andric using namespace ento; 445f757f3fSDimitry Andric 455f757f3fSDimitry Andric namespace { 465f757f3fSDimitry Andric class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> { 475f757f3fSDimitry Andric protected: 485f757f3fSDimitry Andric class PtrCastVisitor : public BugReporterVisitor { 495f757f3fSDimitry Andric public: 505f757f3fSDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 515f757f3fSDimitry Andric static int X = 0; 525f757f3fSDimitry Andric ID.AddPointer(&X); 535f757f3fSDimitry Andric } 545f757f3fSDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 555f757f3fSDimitry Andric BugReporterContext &BRC, 565f757f3fSDimitry Andric PathSensitiveBugReport &BR) override; 575f757f3fSDimitry Andric }; 585f757f3fSDimitry Andric 595f757f3fSDimitry Andric virtual void 605f757f3fSDimitry Andric checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 615f757f3fSDimitry Andric const TypedValueRegion *BaseClassRegion, 625f757f3fSDimitry Andric const SymbolicRegion *DerivedClassRegion) const = 0; 635f757f3fSDimitry Andric 645f757f3fSDimitry Andric public: 655f757f3fSDimitry Andric void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; 665f757f3fSDimitry Andric }; 675f757f3fSDimitry Andric 685f757f3fSDimitry Andric class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker { 69647cbc5dSDimitry Andric const BugType BT{ 70647cbc5dSDimitry Andric this, "Destruction of a polymorphic object with no virtual destructor"}; 715f757f3fSDimitry Andric 725f757f3fSDimitry Andric void 735f757f3fSDimitry Andric checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 745f757f3fSDimitry Andric const TypedValueRegion *BaseClassRegion, 755f757f3fSDimitry Andric const SymbolicRegion *DerivedClassRegion) const override; 765f757f3fSDimitry Andric }; 775f757f3fSDimitry Andric 785f757f3fSDimitry Andric class CXXArrayDeleteChecker : public CXXDeleteChecker { 79647cbc5dSDimitry Andric const BugType BT{this, 80647cbc5dSDimitry Andric "Deleting an array of polymorphic objects is undefined"}; 815f757f3fSDimitry Andric 825f757f3fSDimitry Andric void 835f757f3fSDimitry Andric checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, 845f757f3fSDimitry Andric const TypedValueRegion *BaseClassRegion, 855f757f3fSDimitry Andric const SymbolicRegion *DerivedClassRegion) const override; 865f757f3fSDimitry Andric }; 875f757f3fSDimitry Andric } // namespace 885f757f3fSDimitry Andric 895f757f3fSDimitry Andric void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE, 905f757f3fSDimitry Andric CheckerContext &C) const { 915f757f3fSDimitry Andric const Expr *DeletedObj = DE->getArgument(); 925f757f3fSDimitry Andric const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); 935f757f3fSDimitry Andric if (!MR) 945f757f3fSDimitry Andric return; 955f757f3fSDimitry Andric 965f757f3fSDimitry Andric OverloadedOperatorKind DeleteKind = 975f757f3fSDimitry Andric DE->getOperatorDelete()->getOverloadedOperator(); 985f757f3fSDimitry Andric 995f757f3fSDimitry Andric if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete) 1005f757f3fSDimitry Andric return; 1015f757f3fSDimitry Andric 1025f757f3fSDimitry Andric const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); 1035f757f3fSDimitry Andric const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); 1045f757f3fSDimitry Andric if (!BaseClassRegion || !DerivedClassRegion) 1055f757f3fSDimitry Andric return; 1065f757f3fSDimitry Andric 1075f757f3fSDimitry Andric checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion); 1085f757f3fSDimitry Andric } 1095f757f3fSDimitry Andric 1105f757f3fSDimitry Andric void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr( 1115f757f3fSDimitry Andric const CXXDeleteExpr *DE, CheckerContext &C, 1125f757f3fSDimitry Andric const TypedValueRegion *BaseClassRegion, 1135f757f3fSDimitry Andric const SymbolicRegion *DerivedClassRegion) const { 1145f757f3fSDimitry Andric const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 1155f757f3fSDimitry Andric const auto *DerivedClass = 1165f757f3fSDimitry Andric DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 1175f757f3fSDimitry Andric if (!BaseClass || !DerivedClass) 1185f757f3fSDimitry Andric return; 1195f757f3fSDimitry Andric 1205f757f3fSDimitry Andric if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 1215f757f3fSDimitry Andric return; 1225f757f3fSDimitry Andric 1235f757f3fSDimitry Andric if (BaseClass->getDestructor()->isVirtual()) 1245f757f3fSDimitry Andric return; 1255f757f3fSDimitry Andric 1265f757f3fSDimitry Andric if (!DerivedClass->isDerivedFrom(BaseClass)) 1275f757f3fSDimitry Andric return; 1285f757f3fSDimitry Andric 1295f757f3fSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(); 1305f757f3fSDimitry Andric if (!N) 1315f757f3fSDimitry Andric return; 132647cbc5dSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); 1335f757f3fSDimitry Andric 1345f757f3fSDimitry Andric // Mark region of problematic base class for later use in the BugVisitor. 1355f757f3fSDimitry Andric R->markInteresting(BaseClassRegion); 1365f757f3fSDimitry Andric R->addVisitor<PtrCastVisitor>(); 1375f757f3fSDimitry Andric C.emitReport(std::move(R)); 1385f757f3fSDimitry Andric } 1395f757f3fSDimitry Andric 1405f757f3fSDimitry Andric void CXXArrayDeleteChecker::checkTypedDeleteExpr( 1415f757f3fSDimitry Andric const CXXDeleteExpr *DE, CheckerContext &C, 1425f757f3fSDimitry Andric const TypedValueRegion *BaseClassRegion, 1435f757f3fSDimitry Andric const SymbolicRegion *DerivedClassRegion) const { 1445f757f3fSDimitry Andric const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); 1455f757f3fSDimitry Andric const auto *DerivedClass = 1465f757f3fSDimitry Andric DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); 1475f757f3fSDimitry Andric if (!BaseClass || !DerivedClass) 1485f757f3fSDimitry Andric return; 1495f757f3fSDimitry Andric 1505f757f3fSDimitry Andric if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) 1515f757f3fSDimitry Andric return; 1525f757f3fSDimitry Andric 1535f757f3fSDimitry Andric if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete) 1545f757f3fSDimitry Andric return; 1555f757f3fSDimitry Andric 1565f757f3fSDimitry Andric if (!DerivedClass->isDerivedFrom(BaseClass)) 1575f757f3fSDimitry Andric return; 1585f757f3fSDimitry Andric 1595f757f3fSDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(); 1605f757f3fSDimitry Andric if (!N) 1615f757f3fSDimitry Andric return; 1625f757f3fSDimitry Andric 1635f757f3fSDimitry Andric SmallString<256> Buf; 1645f757f3fSDimitry Andric llvm::raw_svector_ostream OS(Buf); 1655f757f3fSDimitry Andric 1665f757f3fSDimitry Andric QualType SourceType = BaseClassRegion->getValueType(); 1675f757f3fSDimitry Andric QualType TargetType = 1685f757f3fSDimitry Andric DerivedClassRegion->getSymbol()->getType()->getPointeeType(); 1695f757f3fSDimitry Andric 1705f757f3fSDimitry Andric OS << "Deleting an array of '" << TargetType.getAsString() 1715f757f3fSDimitry Andric << "' objects as their base class '" 1725f757f3fSDimitry Andric << SourceType.getAsString(C.getASTContext().getPrintingPolicy()) 1735f757f3fSDimitry Andric << "' is undefined"; 1745f757f3fSDimitry Andric 175647cbc5dSDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); 1765f757f3fSDimitry Andric 1775f757f3fSDimitry Andric // Mark region of problematic base class for later use in the BugVisitor. 1785f757f3fSDimitry Andric R->markInteresting(BaseClassRegion); 1795f757f3fSDimitry Andric R->addVisitor<PtrCastVisitor>(); 1805f757f3fSDimitry Andric C.emitReport(std::move(R)); 1815f757f3fSDimitry Andric } 1825f757f3fSDimitry Andric 1835f757f3fSDimitry Andric PathDiagnosticPieceRef 1845f757f3fSDimitry Andric CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N, 1855f757f3fSDimitry Andric BugReporterContext &BRC, 1865f757f3fSDimitry Andric PathSensitiveBugReport &BR) { 1875f757f3fSDimitry Andric const Stmt *S = N->getStmtForDiagnostics(); 1885f757f3fSDimitry Andric if (!S) 1895f757f3fSDimitry Andric return nullptr; 1905f757f3fSDimitry Andric 1915f757f3fSDimitry Andric const auto *CastE = dyn_cast<CastExpr>(S); 1925f757f3fSDimitry Andric if (!CastE) 1935f757f3fSDimitry Andric return nullptr; 1945f757f3fSDimitry Andric 1955f757f3fSDimitry Andric // FIXME: This way of getting base types does not support reference types. 1965f757f3fSDimitry Andric QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType(); 1975f757f3fSDimitry Andric QualType TargetType = CastE->getType()->getPointeeType(); 1985f757f3fSDimitry Andric 1995f757f3fSDimitry Andric if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType) 2005f757f3fSDimitry Andric return nullptr; 2015f757f3fSDimitry Andric 2025f757f3fSDimitry Andric // Region associated with the current cast expression. 2035f757f3fSDimitry Andric const MemRegion *M = N->getSVal(CastE).getAsRegion(); 2045f757f3fSDimitry Andric if (!M) 2055f757f3fSDimitry Andric return nullptr; 2065f757f3fSDimitry Andric 2075f757f3fSDimitry Andric // Check if target region was marked as problematic previously. 2085f757f3fSDimitry Andric if (!BR.isInteresting(M)) 2095f757f3fSDimitry Andric return nullptr; 2105f757f3fSDimitry Andric 2115f757f3fSDimitry Andric SmallString<256> Buf; 2125f757f3fSDimitry Andric llvm::raw_svector_ostream OS(Buf); 2135f757f3fSDimitry Andric 2145f757f3fSDimitry Andric OS << "Casting from '" << SourceType.getAsString() << "' to '" 2155f757f3fSDimitry Andric << TargetType.getAsString() << "' here"; 2165f757f3fSDimitry Andric 2175f757f3fSDimitry Andric PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 2185f757f3fSDimitry Andric N->getLocationContext()); 2195f757f3fSDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), 2205f757f3fSDimitry Andric /*addPosRange=*/true); 2215f757f3fSDimitry Andric } 2225f757f3fSDimitry Andric 223*0fca6ea1SDimitry Andric void ento::registerArrayDeleteChecker(CheckerManager &mgr) { 2245f757f3fSDimitry Andric mgr.registerChecker<CXXArrayDeleteChecker>(); 2255f757f3fSDimitry Andric } 2265f757f3fSDimitry Andric 227*0fca6ea1SDimitry Andric bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) { 2285f757f3fSDimitry Andric return true; 2295f757f3fSDimitry Andric } 2305f757f3fSDimitry Andric 2315f757f3fSDimitry Andric void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { 2325f757f3fSDimitry Andric mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); 2335f757f3fSDimitry Andric } 2345f757f3fSDimitry Andric 2355f757f3fSDimitry Andric bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( 2365f757f3fSDimitry Andric const CheckerManager &mgr) { 2375f757f3fSDimitry Andric return true; 2385f757f3fSDimitry Andric } 239