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/Checker.h"
35 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
36 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
37 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
38 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
39 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
41 using namespace clang
;
45 class CXXDeleteChecker
: public Checker
<check::PreStmt
<CXXDeleteExpr
>> {
47 class PtrCastVisitor
: public BugReporterVisitor
{
49 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
53 PathDiagnosticPieceRef
VisitNode(const ExplodedNode
*N
,
54 BugReporterContext
&BRC
,
55 PathSensitiveBugReport
&BR
) override
;
59 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
60 const TypedValueRegion
*BaseClassRegion
,
61 const SymbolicRegion
*DerivedClassRegion
) const = 0;
64 void checkPreStmt(const CXXDeleteExpr
*DE
, CheckerContext
&C
) const;
67 class DeleteWithNonVirtualDtorChecker
: public CXXDeleteChecker
{
68 mutable std::unique_ptr
<BugType
> BT
;
71 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
72 const TypedValueRegion
*BaseClassRegion
,
73 const SymbolicRegion
*DerivedClassRegion
) const override
;
76 class CXXArrayDeleteChecker
: public CXXDeleteChecker
{
77 mutable std::unique_ptr
<BugType
> BT
;
80 checkTypedDeleteExpr(const CXXDeleteExpr
*DE
, CheckerContext
&C
,
81 const TypedValueRegion
*BaseClassRegion
,
82 const SymbolicRegion
*DerivedClassRegion
) const override
;
86 void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr
*DE
,
87 CheckerContext
&C
) const {
88 const Expr
*DeletedObj
= DE
->getArgument();
89 const MemRegion
*MR
= C
.getSVal(DeletedObj
).getAsRegion();
93 OverloadedOperatorKind DeleteKind
=
94 DE
->getOperatorDelete()->getOverloadedOperator();
96 if (DeleteKind
!= OO_Delete
&& DeleteKind
!= OO_Array_Delete
)
99 const auto *BaseClassRegion
= MR
->getAs
<TypedValueRegion
>();
100 const auto *DerivedClassRegion
= MR
->getBaseRegion()->getAs
<SymbolicRegion
>();
101 if (!BaseClassRegion
|| !DerivedClassRegion
)
104 checkTypedDeleteExpr(DE
, C
, BaseClassRegion
, DerivedClassRegion
);
107 void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
108 const CXXDeleteExpr
*DE
, CheckerContext
&C
,
109 const TypedValueRegion
*BaseClassRegion
,
110 const SymbolicRegion
*DerivedClassRegion
) const {
111 const auto *BaseClass
= BaseClassRegion
->getValueType()->getAsCXXRecordDecl();
112 const auto *DerivedClass
=
113 DerivedClassRegion
->getSymbol()->getType()->getPointeeCXXRecordDecl();
114 if (!BaseClass
|| !DerivedClass
)
117 if (!BaseClass
->hasDefinition() || !DerivedClass
->hasDefinition())
120 if (BaseClass
->getDestructor()->isVirtual())
123 if (!DerivedClass
->isDerivedFrom(BaseClass
))
127 BT
.reset(new BugType(this,
128 "Destruction of a polymorphic object with no "
129 "virtual destructor",
132 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
136 std::make_unique
<PathSensitiveBugReport
>(*BT
, BT
->getDescription(), N
);
138 // Mark region of problematic base class for later use in the BugVisitor.
139 R
->markInteresting(BaseClassRegion
);
140 R
->addVisitor
<PtrCastVisitor
>();
141 C
.emitReport(std::move(R
));
144 void CXXArrayDeleteChecker::checkTypedDeleteExpr(
145 const CXXDeleteExpr
*DE
, CheckerContext
&C
,
146 const TypedValueRegion
*BaseClassRegion
,
147 const SymbolicRegion
*DerivedClassRegion
) const {
148 const auto *BaseClass
= BaseClassRegion
->getValueType()->getAsCXXRecordDecl();
149 const auto *DerivedClass
=
150 DerivedClassRegion
->getSymbol()->getType()->getPointeeCXXRecordDecl();
151 if (!BaseClass
|| !DerivedClass
)
154 if (!BaseClass
->hasDefinition() || !DerivedClass
->hasDefinition())
157 if (DE
->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete
)
160 if (!DerivedClass
->isDerivedFrom(BaseClass
))
164 BT
.reset(new BugType(this,
165 "Deleting an array of polymorphic objects "
169 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
173 SmallString
<256> Buf
;
174 llvm::raw_svector_ostream
OS(Buf
);
176 QualType SourceType
= BaseClassRegion
->getValueType();
177 QualType TargetType
=
178 DerivedClassRegion
->getSymbol()->getType()->getPointeeType();
180 OS
<< "Deleting an array of '" << TargetType
.getAsString()
181 << "' objects as their base class '"
182 << SourceType
.getAsString(C
.getASTContext().getPrintingPolicy())
185 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT
, OS
.str(), N
);
187 // Mark region of problematic base class for later use in the BugVisitor.
188 R
->markInteresting(BaseClassRegion
);
189 R
->addVisitor
<PtrCastVisitor
>();
190 C
.emitReport(std::move(R
));
193 PathDiagnosticPieceRef
194 CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode
*N
,
195 BugReporterContext
&BRC
,
196 PathSensitiveBugReport
&BR
) {
197 const Stmt
*S
= N
->getStmtForDiagnostics();
201 const auto *CastE
= dyn_cast
<CastExpr
>(S
);
205 // FIXME: This way of getting base types does not support reference types.
206 QualType SourceType
= CastE
->getSubExpr()->getType()->getPointeeType();
207 QualType TargetType
= CastE
->getType()->getPointeeType();
209 if (SourceType
.isNull() || TargetType
.isNull() || SourceType
== TargetType
)
212 // Region associated with the current cast expression.
213 const MemRegion
*M
= N
->getSVal(CastE
).getAsRegion();
217 // Check if target region was marked as problematic previously.
218 if (!BR
.isInteresting(M
))
221 SmallString
<256> Buf
;
222 llvm::raw_svector_ostream
OS(Buf
);
224 OS
<< "Casting from '" << SourceType
.getAsString() << "' to '"
225 << TargetType
.getAsString() << "' here";
227 PathDiagnosticLocation
Pos(S
, BRC
.getSourceManager(),
228 N
->getLocationContext());
229 return std::make_shared
<PathDiagnosticEventPiece
>(Pos
, OS
.str(),
230 /*addPosRange=*/true);
233 void ento::registerCXXArrayDeleteChecker(CheckerManager
&mgr
) {
234 mgr
.registerChecker
<CXXArrayDeleteChecker
>();
237 bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager
&mgr
) {
241 void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager
&mgr
) {
242 mgr
.registerChecker
<DeleteWithNonVirtualDtorChecker
>();
245 bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
246 const CheckerManager
&mgr
) {