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/DynamicTypeMap.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                                      BugReport &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   auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
96 
97   // Mark region of problematic base class for later use in the BugVisitor.
98   R->markInteresting(BaseClassRegion);
99   R->addVisitor(llvm::make_unique<DeleteBugVisitor>());
100   C.emitReport(std::move(R));
101 }
102 
103 PathDiagnosticPieceRef
104 DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
105     const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
106   // Stop traversal after the first conversion was found on a path.
107   if (Satisfied)
108     return nullptr;
109 
110   const Stmt *S = PathDiagnosticLocation::getStmt(N);
111   if (!S)
112     return nullptr;
113 
114   const auto *CastE = dyn_cast<CastExpr>(S);
115   if (!CastE)
116     return nullptr;
117 
118   // Only interested in DerivedToBase implicit casts.
119   // Explicit casts can have different CastKinds.
120   if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) {
121     if (ImplCastE->getCastKind() != CK_DerivedToBase)
122       return nullptr;
123   }
124 
125   // Region associated with the current cast expression.
126   const MemRegion *M = N->getSVal(CastE).getAsRegion();
127   if (!M)
128     return nullptr;
129 
130   // Check if target region was marked as problematic previously.
131   if (!BR.isInteresting(M))
132     return nullptr;
133 
134   // Stop traversal on this path.
135   Satisfied = true;
136 
137   SmallString<256> Buf;
138   llvm::raw_svector_ostream OS(Buf);
139   OS << "Conversion from derived to base happened here";
140   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
141                              N->getLocationContext());
142   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
143                                                     nullptr);
144 }
145 
146 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
147   mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
148 }
149 
150 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
151                                                         const LangOptions &LO) {
152   return true;
153 }
154