diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp new file mode 100644 index 000000000000..1b1226a7f1a7 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp @@ -0,0 +1,238 @@ +//=== CXXDeleteChecker.cpp -------------------------------------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the following new checkers for C++ delete expressions: +// +// * DeleteWithNonVirtualDtorChecker +// Defines a checker for the OOP52-CPP CERT rule: Do not delete a +// polymorphic object without a virtual destructor. +// +// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor +// report if an object with a virtual function but a non-virtual +// destructor exists or is deleted, respectively. +// +// This check exceeds them by comparing the dynamic and static types of +// the object at the point of destruction and only warns if it happens +// through a pointer to a base type without a virtual destructor. The +// check places a note at the last point where the conversion from +// derived to base happened. +// +// * CXXArrayDeleteChecker +// Defines a checker for the EXP51-CPP CERT rule: Do not delete an array +// through a pointer of the incorrect type. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> { +protected: + class PtrCastVisitor : public BugReporterVisitor { + public: + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + } + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + }; + + virtual void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const = 0; + +public: + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; +}; + +class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker { + const BugType BT{ + this, "Destruction of a polymorphic object with no virtual destructor"}; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; + +class CXXArrayDeleteChecker : public CXXDeleteChecker { + const BugType BT{this, + "Deleting an array of polymorphic objects is undefined"}; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; +} // namespace + +void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + const Expr *DeletedObj = DE->getArgument(); + const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); + if (!MR) + return; + + OverloadedOperatorKind DeleteKind = + DE->getOperatorDelete()->getOverloadedOperator(); + + if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete) + return; + + const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); + const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); + if (!BaseClassRegion || !DerivedClassRegion) + return; + + checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion); +} + +void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr( + const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const { + const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); + const auto *DerivedClass = + DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); + if (!BaseClass || !DerivedClass) + return; + + if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) + return; + + if (BaseClass->getDestructor()->isVirtual()) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor<PtrCastVisitor>(); + C.emitReport(std::move(R)); +} + +void CXXArrayDeleteChecker::checkTypedDeleteExpr( + const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const { + const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); + const auto *DerivedClass = + DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); + if (!BaseClass || !DerivedClass) + return; + + if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) + return; + + if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + QualType SourceType = BaseClassRegion->getValueType(); + QualType TargetType = + DerivedClassRegion->getSymbol()->getType()->getPointeeType(); + + OS << "Deleting an array of '" << TargetType.getAsString() + << "' objects as their base class '" + << SourceType.getAsString(C.getASTContext().getPrintingPolicy()) + << "' is undefined"; + + auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor<PtrCastVisitor>(); + C.emitReport(std::move(R)); +} + +PathDiagnosticPieceRef +CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { + const Stmt *S = N->getStmtForDiagnostics(); + if (!S) + return nullptr; + + const auto *CastE = dyn_cast<CastExpr>(S); + if (!CastE) + return nullptr; + + // FIXME: This way of getting base types does not support reference types. + QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType(); + QualType TargetType = CastE->getType()->getPointeeType(); + + if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType) + return nullptr; + + // Region associated with the current cast expression. + const MemRegion *M = N->getSVal(CastE).getAsRegion(); + if (!M) + return nullptr; + + // Check if target region was marked as problematic previously. + if (!BR.isInteresting(M)) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + OS << "Casting from '" << SourceType.getAsString() << "' to '" + << TargetType.getAsString() << "' here"; + + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), + /*addPosRange=*/true); +} + +void ento::registerArrayDeleteChecker(CheckerManager &mgr) { + mgr.registerChecker<CXXArrayDeleteChecker>(); +} + +bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) { + return true; +} + +void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { + mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); +} + +bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( + const CheckerManager &mgr) { + return true; +} |