Home | History | Annotate | Line # | Download | only in Checkers
      1 //===-- DeleteWithNonVirtualDtorChecker.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-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic
     10 // object without a virtual destructor.
     11 //
     12 // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if
     13 // an object with a virtual function but a non-virtual destructor exists or is
     14 // deleted, respectively.
     15 //
     16 // This check exceeds them by comparing the dynamic and static types of the
     17 // object at the point of destruction and only warns if it happens through a
     18 // pointer to a base type without a virtual destructor. The check places a note
     19 // at the last point where the conversion from derived to base happened.
     20 //
     21 //===----------------------------------------------------------------------===//
     22 
     23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     24 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     26 #include "clang/StaticAnalyzer/Core/Checker.h"
     27 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     28 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
     29 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     30 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
     31 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
     32 
     33 using namespace clang;
     34 using namespace ento;
     35 
     36 namespace {
     37 class DeleteWithNonVirtualDtorChecker
     38     : public Checker<check::PreStmt<CXXDeleteExpr>> {
     39   mutable std::unique_ptr<BugType> BT;
     40 
     41   class DeleteBugVisitor : public BugReporterVisitor {
     42   public:
     43     DeleteBugVisitor() : Satisfied(false) {}
     44     void Profile(llvm::FoldingSetNodeID &ID) const override {
     45       static int X = 0;
     46       ID.AddPointer(&X);
     47     }
     48     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
     49                                      BugReporterContext &BRC,
     50                                      PathSensitiveBugReport &BR) override;
     51 
     52   private:
     53     bool Satisfied;
     54   };
     55 
     56 public:
     57   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
     58 };
     59 } // end anonymous namespace
     60 
     61 void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
     62                                                    CheckerContext &C) const {
     63   const Expr *DeletedObj = DE->getArgument();
     64   const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
     65   if (!MR)
     66     return;
     67 
     68   const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
     69   const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
     70   if (!BaseClassRegion || !DerivedClassRegion)
     71     return;
     72 
     73   const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
     74   const auto *DerivedClass =
     75       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
     76   if (!BaseClass || !DerivedClass)
     77     return;
     78 
     79   if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
     80     return;
     81 
     82   if (BaseClass->getDestructor()->isVirtual())
     83     return;
     84 
     85   if (!DerivedClass->isDerivedFrom(BaseClass))
     86     return;
     87 
     88   if (!BT)
     89     BT.reset(new BugType(this,
     90                          "Destruction of a polymorphic object with no "
     91                          "virtual destructor",
     92                          "Logic error"));
     93 
     94   ExplodedNode *N = C.generateNonFatalErrorNode();
     95   if (!N)
     96     return;
     97   auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
     98 
     99   // Mark region of problematic base class for later use in the BugVisitor.
    100   R->markInteresting(BaseClassRegion);
    101   R->addVisitor(std::make_unique<DeleteBugVisitor>());
    102   C.emitReport(std::move(R));
    103 }
    104 
    105 PathDiagnosticPieceRef
    106 DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
    107     const ExplodedNode *N, BugReporterContext &BRC,
    108     PathSensitiveBugReport &BR) {
    109   // Stop traversal after the first conversion was found on a path.
    110   if (Satisfied)
    111     return nullptr;
    112 
    113   const Stmt *S = N->getStmtForDiagnostics();
    114   if (!S)
    115     return nullptr;
    116 
    117   const auto *CastE = dyn_cast<CastExpr>(S);
    118   if (!CastE)
    119     return nullptr;
    120 
    121   // Only interested in DerivedToBase implicit casts.
    122   // Explicit casts can have different CastKinds.
    123   if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) {
    124     if (ImplCastE->getCastKind() != CK_DerivedToBase)
    125       return nullptr;
    126   }
    127 
    128   // Region associated with the current cast expression.
    129   const MemRegion *M = N->getSVal(CastE).getAsRegion();
    130   if (!M)
    131     return nullptr;
    132 
    133   // Check if target region was marked as problematic previously.
    134   if (!BR.isInteresting(M))
    135     return nullptr;
    136 
    137   // Stop traversal on this path.
    138   Satisfied = true;
    139 
    140   SmallString<256> Buf;
    141   llvm::raw_svector_ostream OS(Buf);
    142   OS << "Conversion from derived to base happened here";
    143   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
    144                              N->getLocationContext());
    145   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
    146 }
    147 
    148 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
    149   mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
    150 }
    151 
    152 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
    153                                                     const CheckerManager &mgr) {
    154   return true;
    155 }
    156