xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp (revision 37785fedabd8fa752129ef5bac3462311af91c35)
10e246bb6SViktor Cseh //=== CXXDeleteChecker.cpp -------------------------------------*- C++ -*--===//
271ae858cSViktor Cseh //
371ae858cSViktor Cseh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
471ae858cSViktor Cseh // See https://llvm.org/LICENSE.txt for license information.
571ae858cSViktor Cseh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
671ae858cSViktor Cseh //
771ae858cSViktor Cseh //===----------------------------------------------------------------------===//
871ae858cSViktor Cseh //
90e246bb6SViktor Cseh // This file defines the following new checkers for C++ delete expressions:
1071ae858cSViktor Cseh //
110e246bb6SViktor Cseh //   * DeleteWithNonVirtualDtorChecker
120e246bb6SViktor Cseh //       Defines a checker for the OOP52-CPP CERT rule: Do not delete a
130e246bb6SViktor Cseh //       polymorphic object without a virtual destructor.
1471ae858cSViktor Cseh //
150e246bb6SViktor Cseh //       Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor
160e246bb6SViktor Cseh //       report if an object with a virtual function but a non-virtual
170e246bb6SViktor Cseh //       destructor exists or is deleted, respectively.
180e246bb6SViktor Cseh //
190e246bb6SViktor Cseh //       This check exceeds them by comparing the dynamic and static types of
200e246bb6SViktor Cseh //       the object at the point of destruction and only warns if it happens
210e246bb6SViktor Cseh //       through a pointer to a base type without a virtual destructor. The
220e246bb6SViktor Cseh //       check places a note at the last point where the conversion from
230e246bb6SViktor Cseh //       derived to base happened.
240e246bb6SViktor Cseh //
250e246bb6SViktor Cseh //   * CXXArrayDeleteChecker
260e246bb6SViktor Cseh //       Defines a checker for the EXP51-CPP CERT rule: Do not delete an array
270e246bb6SViktor Cseh //       through a pointer of the incorrect type.
2871ae858cSViktor Cseh //
2971ae858cSViktor Cseh //===----------------------------------------------------------------------===//
3071ae858cSViktor Cseh 
3171ae858cSViktor Cseh #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
3271ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
3371ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
3418f219c5SBalazs Benics #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
3571ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/Checker.h"
3671ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/CheckerManager.h"
3771ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
3871ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
3971ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
4071ae858cSViktor Cseh #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
4171ae858cSViktor Cseh 
4271ae858cSViktor Cseh using namespace clang;
4371ae858cSViktor Cseh using namespace ento;
4471ae858cSViktor Cseh 
4571ae858cSViktor Cseh namespace {
460e246bb6SViktor Cseh class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> {
470e246bb6SViktor Cseh protected:
480e246bb6SViktor Cseh   class PtrCastVisitor : public BugReporterVisitor {
4971ae858cSViktor Cseh   public:
Profile(llvm::FoldingSetNodeID & ID) const5071ae858cSViktor Cseh     void Profile(llvm::FoldingSetNodeID &ID) const override {
5171ae858cSViktor Cseh       static int X = 0;
5271ae858cSViktor Cseh       ID.AddPointer(&X);
5371ae858cSViktor Cseh     }
5471ae858cSViktor Cseh     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
5571ae858cSViktor Cseh                                      BugReporterContext &BRC,
5671ae858cSViktor Cseh                                      PathSensitiveBugReport &BR) override;
5771ae858cSViktor Cseh   };
5871ae858cSViktor Cseh 
590e246bb6SViktor Cseh   virtual void
600e246bb6SViktor Cseh   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
610e246bb6SViktor Cseh                        const TypedValueRegion *BaseClassRegion,
620e246bb6SViktor Cseh                        const SymbolicRegion *DerivedClassRegion) const = 0;
630e246bb6SViktor Cseh 
6471ae858cSViktor Cseh public:
6571ae858cSViktor Cseh   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
6671ae858cSViktor Cseh };
6771ae858cSViktor Cseh 
680e246bb6SViktor Cseh class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker {
6918f219c5SBalazs Benics   const BugType BT{
7018f219c5SBalazs Benics       this, "Destruction of a polymorphic object with no virtual destructor"};
710e246bb6SViktor Cseh 
720e246bb6SViktor Cseh   void
730e246bb6SViktor Cseh   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
740e246bb6SViktor Cseh                        const TypedValueRegion *BaseClassRegion,
750e246bb6SViktor Cseh                        const SymbolicRegion *DerivedClassRegion) const override;
760e246bb6SViktor Cseh };
770e246bb6SViktor Cseh 
780e246bb6SViktor Cseh class CXXArrayDeleteChecker : public CXXDeleteChecker {
7918f219c5SBalazs Benics   const BugType BT{this,
8018f219c5SBalazs Benics                    "Deleting an array of polymorphic objects is undefined"};
810e246bb6SViktor Cseh 
820e246bb6SViktor Cseh   void
830e246bb6SViktor Cseh   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
840e246bb6SViktor Cseh                        const TypedValueRegion *BaseClassRegion,
850e246bb6SViktor Cseh                        const SymbolicRegion *DerivedClassRegion) const override;
860e246bb6SViktor Cseh };
870e246bb6SViktor Cseh } // namespace
880e246bb6SViktor Cseh 
checkPreStmt(const CXXDeleteExpr * DE,CheckerContext & C) const890e246bb6SViktor Cseh void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE,
9071ae858cSViktor Cseh                                     CheckerContext &C) const {
9171ae858cSViktor Cseh   const Expr *DeletedObj = DE->getArgument();
9271ae858cSViktor Cseh   const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
9371ae858cSViktor Cseh   if (!MR)
9471ae858cSViktor Cseh     return;
9571ae858cSViktor Cseh 
960e246bb6SViktor Cseh   OverloadedOperatorKind DeleteKind =
970e246bb6SViktor Cseh       DE->getOperatorDelete()->getOverloadedOperator();
980e246bb6SViktor Cseh 
990e246bb6SViktor Cseh   if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete)
1000e246bb6SViktor Cseh     return;
1010e246bb6SViktor Cseh 
10271ae858cSViktor Cseh   const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
10371ae858cSViktor Cseh   const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
10471ae858cSViktor Cseh   if (!BaseClassRegion || !DerivedClassRegion)
10571ae858cSViktor Cseh     return;
10671ae858cSViktor Cseh 
1070e246bb6SViktor Cseh   checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion);
1080e246bb6SViktor Cseh }
1090e246bb6SViktor Cseh 
checkTypedDeleteExpr(const CXXDeleteExpr * DE,CheckerContext & C,const TypedValueRegion * BaseClassRegion,const SymbolicRegion * DerivedClassRegion) const1100e246bb6SViktor Cseh void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
1110e246bb6SViktor Cseh     const CXXDeleteExpr *DE, CheckerContext &C,
1120e246bb6SViktor Cseh     const TypedValueRegion *BaseClassRegion,
1130e246bb6SViktor Cseh     const SymbolicRegion *DerivedClassRegion) const {
11471ae858cSViktor Cseh   const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
11571ae858cSViktor Cseh   const auto *DerivedClass =
11671ae858cSViktor Cseh       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
11771ae858cSViktor Cseh   if (!BaseClass || !DerivedClass)
11871ae858cSViktor Cseh     return;
11971ae858cSViktor Cseh 
120564e0165SDiscookie   if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
121564e0165SDiscookie     return;
122564e0165SDiscookie 
12371ae858cSViktor Cseh   if (BaseClass->getDestructor()->isVirtual())
12471ae858cSViktor Cseh     return;
12571ae858cSViktor Cseh 
12671ae858cSViktor Cseh   if (!DerivedClass->isDerivedFrom(BaseClass))
12771ae858cSViktor Cseh     return;
12871ae858cSViktor Cseh 
12971ae858cSViktor Cseh   ExplodedNode *N = C.generateNonFatalErrorNode();
13071ae858cSViktor Cseh   if (!N)
13171ae858cSViktor Cseh     return;
13218f219c5SBalazs Benics   auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
13371ae858cSViktor Cseh 
13471ae858cSViktor Cseh   // Mark region of problematic base class for later use in the BugVisitor.
13571ae858cSViktor Cseh   R->markInteresting(BaseClassRegion);
1360e246bb6SViktor Cseh   R->addVisitor<PtrCastVisitor>();
1370e246bb6SViktor Cseh   C.emitReport(std::move(R));
1380e246bb6SViktor Cseh }
1390e246bb6SViktor Cseh 
checkTypedDeleteExpr(const CXXDeleteExpr * DE,CheckerContext & C,const TypedValueRegion * BaseClassRegion,const SymbolicRegion * DerivedClassRegion) const1400e246bb6SViktor Cseh void CXXArrayDeleteChecker::checkTypedDeleteExpr(
1410e246bb6SViktor Cseh     const CXXDeleteExpr *DE, CheckerContext &C,
1420e246bb6SViktor Cseh     const TypedValueRegion *BaseClassRegion,
1430e246bb6SViktor Cseh     const SymbolicRegion *DerivedClassRegion) const {
1440e246bb6SViktor Cseh   const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
1450e246bb6SViktor Cseh   const auto *DerivedClass =
1460e246bb6SViktor Cseh       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
1470e246bb6SViktor Cseh   if (!BaseClass || !DerivedClass)
1480e246bb6SViktor Cseh     return;
1490e246bb6SViktor Cseh 
150564e0165SDiscookie   if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
151564e0165SDiscookie     return;
152564e0165SDiscookie 
1530e246bb6SViktor Cseh   if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete)
1540e246bb6SViktor Cseh     return;
1550e246bb6SViktor Cseh 
1560e246bb6SViktor Cseh   if (!DerivedClass->isDerivedFrom(BaseClass))
1570e246bb6SViktor Cseh     return;
1580e246bb6SViktor Cseh 
1590e246bb6SViktor Cseh   ExplodedNode *N = C.generateNonFatalErrorNode();
1600e246bb6SViktor Cseh   if (!N)
1610e246bb6SViktor Cseh     return;
1620e246bb6SViktor Cseh 
1630e246bb6SViktor Cseh   SmallString<256> Buf;
1640e246bb6SViktor Cseh   llvm::raw_svector_ostream OS(Buf);
1650e246bb6SViktor Cseh 
1660e246bb6SViktor Cseh   QualType SourceType = BaseClassRegion->getValueType();
1670e246bb6SViktor Cseh   QualType TargetType =
1680e246bb6SViktor Cseh       DerivedClassRegion->getSymbol()->getType()->getPointeeType();
1690e246bb6SViktor Cseh 
1700e246bb6SViktor Cseh   OS << "Deleting an array of '" << TargetType.getAsString()
1710e246bb6SViktor Cseh      << "' objects as their base class '"
1720e246bb6SViktor Cseh      << SourceType.getAsString(C.getASTContext().getPrintingPolicy())
1730e246bb6SViktor Cseh      << "' is undefined";
1740e246bb6SViktor Cseh 
17518f219c5SBalazs Benics   auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
1760e246bb6SViktor Cseh 
1770e246bb6SViktor Cseh   // Mark region of problematic base class for later use in the BugVisitor.
1780e246bb6SViktor Cseh   R->markInteresting(BaseClassRegion);
1790e246bb6SViktor Cseh   R->addVisitor<PtrCastVisitor>();
18071ae858cSViktor Cseh   C.emitReport(std::move(R));
18171ae858cSViktor Cseh }
18271ae858cSViktor Cseh 
18371ae858cSViktor Cseh PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)1840e246bb6SViktor Cseh CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N,
1850e246bb6SViktor Cseh                                             BugReporterContext &BRC,
18671ae858cSViktor Cseh                                             PathSensitiveBugReport &BR) {
18771ae858cSViktor Cseh   const Stmt *S = N->getStmtForDiagnostics();
18871ae858cSViktor Cseh   if (!S)
18971ae858cSViktor Cseh     return nullptr;
19071ae858cSViktor Cseh 
19171ae858cSViktor Cseh   const auto *CastE = dyn_cast<CastExpr>(S);
19271ae858cSViktor Cseh   if (!CastE)
19371ae858cSViktor Cseh     return nullptr;
19471ae858cSViktor Cseh 
1950e246bb6SViktor Cseh   // FIXME: This way of getting base types does not support reference types.
1960e246bb6SViktor Cseh   QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType();
1970e246bb6SViktor Cseh   QualType TargetType = CastE->getType()->getPointeeType();
1980e246bb6SViktor Cseh 
1990e246bb6SViktor Cseh   if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType)
20071ae858cSViktor Cseh     return nullptr;
20171ae858cSViktor Cseh 
20271ae858cSViktor Cseh   // Region associated with the current cast expression.
20371ae858cSViktor Cseh   const MemRegion *M = N->getSVal(CastE).getAsRegion();
20471ae858cSViktor Cseh   if (!M)
20571ae858cSViktor Cseh     return nullptr;
20671ae858cSViktor Cseh 
20771ae858cSViktor Cseh   // Check if target region was marked as problematic previously.
20871ae858cSViktor Cseh   if (!BR.isInteresting(M))
20971ae858cSViktor Cseh     return nullptr;
21071ae858cSViktor Cseh 
21171ae858cSViktor Cseh   SmallString<256> Buf;
21271ae858cSViktor Cseh   llvm::raw_svector_ostream OS(Buf);
2130e246bb6SViktor Cseh 
2140e246bb6SViktor Cseh   OS << "Casting from '" << SourceType.getAsString() << "' to '"
2150e246bb6SViktor Cseh      << TargetType.getAsString() << "' here";
2160e246bb6SViktor Cseh 
21771ae858cSViktor Cseh   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
21871ae858cSViktor Cseh                              N->getLocationContext());
2190e246bb6SViktor Cseh   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
2200e246bb6SViktor Cseh                                                     /*addPosRange=*/true);
2210e246bb6SViktor Cseh }
2220e246bb6SViktor Cseh 
registerArrayDeleteChecker(CheckerManager & mgr)223*37785fedSDiscookie void ento::registerArrayDeleteChecker(CheckerManager &mgr) {
2240e246bb6SViktor Cseh   mgr.registerChecker<CXXArrayDeleteChecker>();
2250e246bb6SViktor Cseh }
2260e246bb6SViktor Cseh 
shouldRegisterArrayDeleteChecker(const CheckerManager & mgr)227*37785fedSDiscookie bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager &mgr) {
2280e246bb6SViktor Cseh   return true;
22971ae858cSViktor Cseh }
23071ae858cSViktor Cseh 
registerDeleteWithNonVirtualDtorChecker(CheckerManager & mgr)23171ae858cSViktor Cseh void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
23271ae858cSViktor Cseh   mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
23371ae858cSViktor Cseh }
23471ae858cSViktor Cseh 
shouldRegisterDeleteWithNonVirtualDtorChecker(const CheckerManager & mgr)23571ae858cSViktor Cseh bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
23671ae858cSViktor Cseh     const CheckerManager &mgr) {
23771ae858cSViktor Cseh   return true;
23871ae858cSViktor Cseh }
239