xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1*5f757f3fSDimitry Andric //=== CXXDeleteChecker.cpp -------------------------------------*- C++ -*--===//
2*5f757f3fSDimitry Andric //
3*5f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5f757f3fSDimitry Andric //
7*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
8*5f757f3fSDimitry Andric //
9*5f757f3fSDimitry Andric // This file defines the following new checkers for C++ delete expressions:
10*5f757f3fSDimitry Andric //
11*5f757f3fSDimitry Andric //   * DeleteWithNonVirtualDtorChecker
12*5f757f3fSDimitry Andric //       Defines a checker for the OOP52-CPP CERT rule: Do not delete a
13*5f757f3fSDimitry Andric //       polymorphic object without a virtual destructor.
14*5f757f3fSDimitry Andric //
15*5f757f3fSDimitry Andric //       Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor
16*5f757f3fSDimitry Andric //       report if an object with a virtual function but a non-virtual
17*5f757f3fSDimitry Andric //       destructor exists or is deleted, respectively.
18*5f757f3fSDimitry Andric //
19*5f757f3fSDimitry Andric //       This check exceeds them by comparing the dynamic and static types of
20*5f757f3fSDimitry Andric //       the object at the point of destruction and only warns if it happens
21*5f757f3fSDimitry Andric //       through a pointer to a base type without a virtual destructor. The
22*5f757f3fSDimitry Andric //       check places a note at the last point where the conversion from
23*5f757f3fSDimitry Andric //       derived to base happened.
24*5f757f3fSDimitry Andric //
25*5f757f3fSDimitry Andric //   * CXXArrayDeleteChecker
26*5f757f3fSDimitry Andric //       Defines a checker for the EXP51-CPP CERT rule: Do not delete an array
27*5f757f3fSDimitry Andric //       through a pointer of the incorrect type.
28*5f757f3fSDimitry Andric //
29*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
30*5f757f3fSDimitry Andric 
31*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
32*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
33*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
34*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
35*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
36*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
37*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
38*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
39*5f757f3fSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
40*5f757f3fSDimitry Andric 
41*5f757f3fSDimitry Andric using namespace clang;
42*5f757f3fSDimitry Andric using namespace ento;
43*5f757f3fSDimitry Andric 
44*5f757f3fSDimitry Andric namespace {
45*5f757f3fSDimitry Andric class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> {
46*5f757f3fSDimitry Andric protected:
47*5f757f3fSDimitry Andric   class PtrCastVisitor : public BugReporterVisitor {
48*5f757f3fSDimitry Andric   public:
49*5f757f3fSDimitry Andric     void Profile(llvm::FoldingSetNodeID &ID) const override {
50*5f757f3fSDimitry Andric       static int X = 0;
51*5f757f3fSDimitry Andric       ID.AddPointer(&X);
52*5f757f3fSDimitry Andric     }
53*5f757f3fSDimitry Andric     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
54*5f757f3fSDimitry Andric                                      BugReporterContext &BRC,
55*5f757f3fSDimitry Andric                                      PathSensitiveBugReport &BR) override;
56*5f757f3fSDimitry Andric   };
57*5f757f3fSDimitry Andric 
58*5f757f3fSDimitry Andric   virtual void
59*5f757f3fSDimitry Andric   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
60*5f757f3fSDimitry Andric                        const TypedValueRegion *BaseClassRegion,
61*5f757f3fSDimitry Andric                        const SymbolicRegion *DerivedClassRegion) const = 0;
62*5f757f3fSDimitry Andric 
63*5f757f3fSDimitry Andric public:
64*5f757f3fSDimitry Andric   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
65*5f757f3fSDimitry Andric };
66*5f757f3fSDimitry Andric 
67*5f757f3fSDimitry Andric class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker {
68*5f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT;
69*5f757f3fSDimitry Andric 
70*5f757f3fSDimitry Andric   void
71*5f757f3fSDimitry Andric   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
72*5f757f3fSDimitry Andric                        const TypedValueRegion *BaseClassRegion,
73*5f757f3fSDimitry Andric                        const SymbolicRegion *DerivedClassRegion) const override;
74*5f757f3fSDimitry Andric };
75*5f757f3fSDimitry Andric 
76*5f757f3fSDimitry Andric class CXXArrayDeleteChecker : public CXXDeleteChecker {
77*5f757f3fSDimitry Andric   mutable std::unique_ptr<BugType> BT;
78*5f757f3fSDimitry Andric 
79*5f757f3fSDimitry Andric   void
80*5f757f3fSDimitry Andric   checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
81*5f757f3fSDimitry Andric                        const TypedValueRegion *BaseClassRegion,
82*5f757f3fSDimitry Andric                        const SymbolicRegion *DerivedClassRegion) const override;
83*5f757f3fSDimitry Andric };
84*5f757f3fSDimitry Andric } // namespace
85*5f757f3fSDimitry Andric 
86*5f757f3fSDimitry Andric void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE,
87*5f757f3fSDimitry Andric                                     CheckerContext &C) const {
88*5f757f3fSDimitry Andric   const Expr *DeletedObj = DE->getArgument();
89*5f757f3fSDimitry Andric   const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
90*5f757f3fSDimitry Andric   if (!MR)
91*5f757f3fSDimitry Andric     return;
92*5f757f3fSDimitry Andric 
93*5f757f3fSDimitry Andric   OverloadedOperatorKind DeleteKind =
94*5f757f3fSDimitry Andric       DE->getOperatorDelete()->getOverloadedOperator();
95*5f757f3fSDimitry Andric 
96*5f757f3fSDimitry Andric   if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete)
97*5f757f3fSDimitry Andric     return;
98*5f757f3fSDimitry Andric 
99*5f757f3fSDimitry Andric   const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
100*5f757f3fSDimitry Andric   const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
101*5f757f3fSDimitry Andric   if (!BaseClassRegion || !DerivedClassRegion)
102*5f757f3fSDimitry Andric     return;
103*5f757f3fSDimitry Andric 
104*5f757f3fSDimitry Andric   checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion);
105*5f757f3fSDimitry Andric }
106*5f757f3fSDimitry Andric 
107*5f757f3fSDimitry Andric void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
108*5f757f3fSDimitry Andric     const CXXDeleteExpr *DE, CheckerContext &C,
109*5f757f3fSDimitry Andric     const TypedValueRegion *BaseClassRegion,
110*5f757f3fSDimitry Andric     const SymbolicRegion *DerivedClassRegion) const {
111*5f757f3fSDimitry Andric   const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
112*5f757f3fSDimitry Andric   const auto *DerivedClass =
113*5f757f3fSDimitry Andric       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
114*5f757f3fSDimitry Andric   if (!BaseClass || !DerivedClass)
115*5f757f3fSDimitry Andric     return;
116*5f757f3fSDimitry Andric 
117*5f757f3fSDimitry Andric   if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
118*5f757f3fSDimitry Andric     return;
119*5f757f3fSDimitry Andric 
120*5f757f3fSDimitry Andric   if (BaseClass->getDestructor()->isVirtual())
121*5f757f3fSDimitry Andric     return;
122*5f757f3fSDimitry Andric 
123*5f757f3fSDimitry Andric   if (!DerivedClass->isDerivedFrom(BaseClass))
124*5f757f3fSDimitry Andric     return;
125*5f757f3fSDimitry Andric 
126*5f757f3fSDimitry Andric   if (!BT)
127*5f757f3fSDimitry Andric     BT.reset(new BugType(this,
128*5f757f3fSDimitry Andric                          "Destruction of a polymorphic object with no "
129*5f757f3fSDimitry Andric                          "virtual destructor",
130*5f757f3fSDimitry Andric                          "Logic error"));
131*5f757f3fSDimitry Andric 
132*5f757f3fSDimitry Andric   ExplodedNode *N = C.generateNonFatalErrorNode();
133*5f757f3fSDimitry Andric   if (!N)
134*5f757f3fSDimitry Andric     return;
135*5f757f3fSDimitry Andric   auto R =
136*5f757f3fSDimitry Andric       std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
137*5f757f3fSDimitry Andric 
138*5f757f3fSDimitry Andric   // Mark region of problematic base class for later use in the BugVisitor.
139*5f757f3fSDimitry Andric   R->markInteresting(BaseClassRegion);
140*5f757f3fSDimitry Andric   R->addVisitor<PtrCastVisitor>();
141*5f757f3fSDimitry Andric   C.emitReport(std::move(R));
142*5f757f3fSDimitry Andric }
143*5f757f3fSDimitry Andric 
144*5f757f3fSDimitry Andric void CXXArrayDeleteChecker::checkTypedDeleteExpr(
145*5f757f3fSDimitry Andric     const CXXDeleteExpr *DE, CheckerContext &C,
146*5f757f3fSDimitry Andric     const TypedValueRegion *BaseClassRegion,
147*5f757f3fSDimitry Andric     const SymbolicRegion *DerivedClassRegion) const {
148*5f757f3fSDimitry Andric   const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
149*5f757f3fSDimitry Andric   const auto *DerivedClass =
150*5f757f3fSDimitry Andric       DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
151*5f757f3fSDimitry Andric   if (!BaseClass || !DerivedClass)
152*5f757f3fSDimitry Andric     return;
153*5f757f3fSDimitry Andric 
154*5f757f3fSDimitry Andric   if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
155*5f757f3fSDimitry Andric     return;
156*5f757f3fSDimitry Andric 
157*5f757f3fSDimitry Andric   if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete)
158*5f757f3fSDimitry Andric     return;
159*5f757f3fSDimitry Andric 
160*5f757f3fSDimitry Andric   if (!DerivedClass->isDerivedFrom(BaseClass))
161*5f757f3fSDimitry Andric     return;
162*5f757f3fSDimitry Andric 
163*5f757f3fSDimitry Andric   if (!BT)
164*5f757f3fSDimitry Andric     BT.reset(new BugType(this,
165*5f757f3fSDimitry Andric                          "Deleting an array of polymorphic objects "
166*5f757f3fSDimitry Andric                          "is undefined",
167*5f757f3fSDimitry Andric                          "Logic error"));
168*5f757f3fSDimitry Andric 
169*5f757f3fSDimitry Andric   ExplodedNode *N = C.generateNonFatalErrorNode();
170*5f757f3fSDimitry Andric   if (!N)
171*5f757f3fSDimitry Andric     return;
172*5f757f3fSDimitry Andric 
173*5f757f3fSDimitry Andric   SmallString<256> Buf;
174*5f757f3fSDimitry Andric   llvm::raw_svector_ostream OS(Buf);
175*5f757f3fSDimitry Andric 
176*5f757f3fSDimitry Andric   QualType SourceType = BaseClassRegion->getValueType();
177*5f757f3fSDimitry Andric   QualType TargetType =
178*5f757f3fSDimitry Andric       DerivedClassRegion->getSymbol()->getType()->getPointeeType();
179*5f757f3fSDimitry Andric 
180*5f757f3fSDimitry Andric   OS << "Deleting an array of '" << TargetType.getAsString()
181*5f757f3fSDimitry Andric      << "' objects as their base class '"
182*5f757f3fSDimitry Andric      << SourceType.getAsString(C.getASTContext().getPrintingPolicy())
183*5f757f3fSDimitry Andric      << "' is undefined";
184*5f757f3fSDimitry Andric 
185*5f757f3fSDimitry Andric   auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
186*5f757f3fSDimitry Andric 
187*5f757f3fSDimitry Andric   // Mark region of problematic base class for later use in the BugVisitor.
188*5f757f3fSDimitry Andric   R->markInteresting(BaseClassRegion);
189*5f757f3fSDimitry Andric   R->addVisitor<PtrCastVisitor>();
190*5f757f3fSDimitry Andric   C.emitReport(std::move(R));
191*5f757f3fSDimitry Andric }
192*5f757f3fSDimitry Andric 
193*5f757f3fSDimitry Andric PathDiagnosticPieceRef
194*5f757f3fSDimitry Andric CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N,
195*5f757f3fSDimitry Andric                                             BugReporterContext &BRC,
196*5f757f3fSDimitry Andric                                             PathSensitiveBugReport &BR) {
197*5f757f3fSDimitry Andric   const Stmt *S = N->getStmtForDiagnostics();
198*5f757f3fSDimitry Andric   if (!S)
199*5f757f3fSDimitry Andric     return nullptr;
200*5f757f3fSDimitry Andric 
201*5f757f3fSDimitry Andric   const auto *CastE = dyn_cast<CastExpr>(S);
202*5f757f3fSDimitry Andric   if (!CastE)
203*5f757f3fSDimitry Andric     return nullptr;
204*5f757f3fSDimitry Andric 
205*5f757f3fSDimitry Andric   // FIXME: This way of getting base types does not support reference types.
206*5f757f3fSDimitry Andric   QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType();
207*5f757f3fSDimitry Andric   QualType TargetType = CastE->getType()->getPointeeType();
208*5f757f3fSDimitry Andric 
209*5f757f3fSDimitry Andric   if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType)
210*5f757f3fSDimitry Andric     return nullptr;
211*5f757f3fSDimitry Andric 
212*5f757f3fSDimitry Andric   // Region associated with the current cast expression.
213*5f757f3fSDimitry Andric   const MemRegion *M = N->getSVal(CastE).getAsRegion();
214*5f757f3fSDimitry Andric   if (!M)
215*5f757f3fSDimitry Andric     return nullptr;
216*5f757f3fSDimitry Andric 
217*5f757f3fSDimitry Andric   // Check if target region was marked as problematic previously.
218*5f757f3fSDimitry Andric   if (!BR.isInteresting(M))
219*5f757f3fSDimitry Andric     return nullptr;
220*5f757f3fSDimitry Andric 
221*5f757f3fSDimitry Andric   SmallString<256> Buf;
222*5f757f3fSDimitry Andric   llvm::raw_svector_ostream OS(Buf);
223*5f757f3fSDimitry Andric 
224*5f757f3fSDimitry Andric   OS << "Casting from '" << SourceType.getAsString() << "' to '"
225*5f757f3fSDimitry Andric      << TargetType.getAsString() << "' here";
226*5f757f3fSDimitry Andric 
227*5f757f3fSDimitry Andric   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
228*5f757f3fSDimitry Andric                              N->getLocationContext());
229*5f757f3fSDimitry Andric   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
230*5f757f3fSDimitry Andric                                                     /*addPosRange=*/true);
231*5f757f3fSDimitry Andric }
232*5f757f3fSDimitry Andric 
233*5f757f3fSDimitry Andric void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) {
234*5f757f3fSDimitry Andric   mgr.registerChecker<CXXArrayDeleteChecker>();
235*5f757f3fSDimitry Andric }
236*5f757f3fSDimitry Andric 
237*5f757f3fSDimitry Andric bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) {
238*5f757f3fSDimitry Andric   return true;
239*5f757f3fSDimitry Andric }
240*5f757f3fSDimitry Andric 
241*5f757f3fSDimitry Andric void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
242*5f757f3fSDimitry Andric   mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
243*5f757f3fSDimitry Andric }
244*5f757f3fSDimitry Andric 
245*5f757f3fSDimitry Andric bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
246*5f757f3fSDimitry Andric     const CheckerManager &mgr) {
247*5f757f3fSDimitry Andric   return true;
248*5f757f3fSDimitry Andric }
249