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:
DeleteBugVisitor()43     DeleteBugVisitor() : Satisfied(false) {}
Profile(llvm::FoldingSetNodeID & ID) const44     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 
checkPreStmt(const CXXDeleteExpr * DE,CheckerContext & C) const61 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
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)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 
registerDeleteWithNonVirtualDtorChecker(CheckerManager & mgr)148 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
149   mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
150 }
151 
shouldRegisterDeleteWithNonVirtualDtorChecker(const CheckerManager & mgr)152 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
153                                                     const CheckerManager &mgr) {
154   return true;
155 }
156