1 //=== CXXDeleteChecker.cpp -------------------------------------*- C++ -*--===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines the following new checkers for C++ delete expressions:
11 // * DeleteWithNonVirtualDtorChecker
12 // Defines a checker for the OOP52-CPP CERT rule: Do not delete a
13 // polymorphic object without a virtual destructor.
15 // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor
16 // report if an object with a virtual function but a non-virtual
17 // destructor exists or is deleted, respectively.
19 // This check exceeds them by comparing the dynamic and static types of
20 // the object at the point of destruction and only warns if it happens
21 // through a pointer to a base type without a virtual destructor. The
22 // check places a note at the last point where the conversion from
23 // derived to base happened.
25 // * CXXArrayDeleteChecker
26 // Defines a checker for the EXP51-CPP CERT rule: Do not delete an array
27 // through a pointer of the incorrect type.
29 //===----------------------------------------------------------------------===//
31 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
32 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
33 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
34 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
35 #include "clang/StaticAnalyzer/Core/Checker.h"
36 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
37 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
38 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
39 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
40 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
42 using namespace clang
;
46 class CXXDeleteChecker
: public Checker
<check::PreStmt
<CXXDeleteExpr
>> {
48 class PtrCastVisitor
: public BugReporterVisitor
{
50 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
54 PathDiagnosticPieceRef
VisitNode(const ExplodedNode
*N
,
55 BugReporterContext
&BRC
,
56 PathSensitiveBugReport
&BR
) override
;
60 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
61 const TypedValueRegion
*BaseClassRegion
,
62 const SymbolicRegion
*DerivedClassRegion
) const = 0;
65 void checkPreStmt(const CXXDeleteExpr
*DE
, CheckerContext
&C
) const;
68 class DeleteWithNonVirtualDtorChecker
: public CXXDeleteChecker
{
70 this, "Destruction of a polymorphic object with no virtual destructor"};
73 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
74 const TypedValueRegion
*BaseClassRegion
,
75 const SymbolicRegion
*DerivedClassRegion
) const override
;
78 class CXXArrayDeleteChecker
: public CXXDeleteChecker
{
79 const BugType BT
{this,
80 "Deleting an array of polymorphic objects is undefined"};
83 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
84 const TypedValueRegion
*BaseClassRegion
,
85 const SymbolicRegion
*DerivedClassRegion
) const override
;
89 void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr
*DE
,
90 CheckerContext
&C
) const {
91 const Expr
*DeletedObj
= DE
->getArgument();
92 const MemRegion
*MR
= C
.getSVal(DeletedObj
).getAsRegion();
96 OverloadedOperatorKind DeleteKind
=
97 DE
->getOperatorDelete()->getOverloadedOperator();
99 if (DeleteKind
!= OO_Delete
&& DeleteKind
!= OO_Array_Delete
)
102 const auto *BaseClassRegion
= MR
->getAs
<TypedValueRegion
>();
103 const auto *DerivedClassRegion
= MR
->getBaseRegion()->getAs
<SymbolicRegion
>();
104 if (!BaseClassRegion
|| !DerivedClassRegion
)
107 checkTypedDeleteExpr(DE
, C
, BaseClassRegion
, DerivedClassRegion
);
110 void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
111 const CXXDeleteExpr
*DE
, CheckerContext
&C
,
112 const TypedValueRegion
*BaseClassRegion
,
113 const SymbolicRegion
*DerivedClassRegion
) const {
114 const auto *BaseClass
= BaseClassRegion
->getValueType()->getAsCXXRecordDecl();
115 const auto *DerivedClass
=
116 DerivedClassRegion
->getSymbol()->getType()->getPointeeCXXRecordDecl();
117 if (!BaseClass
|| !DerivedClass
)
120 if (!BaseClass
->hasDefinition() || !DerivedClass
->hasDefinition())
123 if (BaseClass
->getDestructor()->isVirtual())
126 if (!DerivedClass
->isDerivedFrom(BaseClass
))
129 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
132 auto R
= std::make_unique
<PathSensitiveBugReport
>(BT
, BT
.getDescription(), N
);
134 // Mark region of problematic base class for later use in the BugVisitor.
135 R
->markInteresting(BaseClassRegion
);
136 R
->addVisitor
<PtrCastVisitor
>();
137 C
.emitReport(std::move(R
));
140 void CXXArrayDeleteChecker::checkTypedDeleteExpr(
141 const CXXDeleteExpr
*DE
, CheckerContext
&C
,
142 const TypedValueRegion
*BaseClassRegion
,
143 const SymbolicRegion
*DerivedClassRegion
) const {
144 const auto *BaseClass
= BaseClassRegion
->getValueType()->getAsCXXRecordDecl();
145 const auto *DerivedClass
=
146 DerivedClassRegion
->getSymbol()->getType()->getPointeeCXXRecordDecl();
147 if (!BaseClass
|| !DerivedClass
)
150 if (!BaseClass
->hasDefinition() || !DerivedClass
->hasDefinition())
153 if (DE
->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete
)
156 if (!DerivedClass
->isDerivedFrom(BaseClass
))
159 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
163 SmallString
<256> Buf
;
164 llvm::raw_svector_ostream
OS(Buf
);
166 QualType SourceType
= BaseClassRegion
->getValueType();
167 QualType TargetType
=
168 DerivedClassRegion
->getSymbol()->getType()->getPointeeType();
170 OS
<< "Deleting an array of '" << TargetType
.getAsString()
171 << "' objects as their base class '"
172 << SourceType
.getAsString(C
.getASTContext().getPrintingPolicy())
175 auto R
= std::make_unique
<PathSensitiveBugReport
>(BT
, OS
.str(), N
);
177 // Mark region of problematic base class for later use in the BugVisitor.
178 R
->markInteresting(BaseClassRegion
);
179 R
->addVisitor
<PtrCastVisitor
>();
180 C
.emitReport(std::move(R
));
183 PathDiagnosticPieceRef
184 CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode
*N
,
185 BugReporterContext
&BRC
,
186 PathSensitiveBugReport
&BR
) {
187 const Stmt
*S
= N
->getStmtForDiagnostics();
191 const auto *CastE
= dyn_cast
<CastExpr
>(S
);
195 // FIXME: This way of getting base types does not support reference types.
196 QualType SourceType
= CastE
->getSubExpr()->getType()->getPointeeType();
197 QualType TargetType
= CastE
->getType()->getPointeeType();
199 if (SourceType
.isNull() || TargetType
.isNull() || SourceType
== TargetType
)
202 // Region associated with the current cast expression.
203 const MemRegion
*M
= N
->getSVal(CastE
).getAsRegion();
207 // Check if target region was marked as problematic previously.
208 if (!BR
.isInteresting(M
))
211 SmallString
<256> Buf
;
212 llvm::raw_svector_ostream
OS(Buf
);
214 OS
<< "Casting from '" << SourceType
.getAsString() << "' to '"
215 << TargetType
.getAsString() << "' here";
217 PathDiagnosticLocation
Pos(S
, BRC
.getSourceManager(),
218 N
->getLocationContext());
219 return std::make_shared
<PathDiagnosticEventPiece
>(Pos
, OS
.str(),
220 /*addPosRange=*/true);
223 void ento::registerArrayDeleteChecker(CheckerManager
&mgr
) {
224 mgr
.registerChecker
<CXXArrayDeleteChecker
>();
227 bool ento::shouldRegisterArrayDeleteChecker(const CheckerManager
&mgr
) {
231 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager
&mgr
) {
232 mgr
.registerChecker
<DeleteWithNonVirtualDtorChecker
>();
235 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
236 const CheckerManager
&mgr
) {