/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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: |
43 | 13 | DeleteBugVisitor() = default; |
44 | 13 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
45 | 13 | static int X = 0; |
46 | 13 | ID.AddPointer(&X); |
47 | 13 | } |
48 | | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
49 | | BugReporterContext &BRC, |
50 | | PathSensitiveBugReport &BR) override; |
51 | | |
52 | | private: |
53 | | bool Satisfied = false; |
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 | 23 | CheckerContext &C) const { |
63 | 23 | const Expr *DeletedObj = DE->getArgument(); |
64 | 23 | const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); |
65 | 23 | if (!MR) |
66 | 0 | return; |
67 | | |
68 | 23 | const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); |
69 | 23 | const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); |
70 | 23 | if (!BaseClassRegion || !DerivedClassRegion19 ) |
71 | 5 | return; |
72 | | |
73 | 18 | const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); |
74 | 18 | const auto *DerivedClass = |
75 | 18 | DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); |
76 | 18 | if (!BaseClass || !DerivedClass) |
77 | 0 | return; |
78 | | |
79 | 18 | if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) |
80 | 0 | return; |
81 | | |
82 | 18 | if (BaseClass->getDestructor()->isVirtual()) |
83 | 2 | return; |
84 | | |
85 | 16 | if (!DerivedClass->isDerivedFrom(BaseClass)) |
86 | 3 | return; |
87 | | |
88 | 13 | if (!BT) |
89 | 1 | BT.reset(new BugType(this, |
90 | 1 | "Destruction of a polymorphic object with no " |
91 | 1 | "virtual destructor", |
92 | 1 | "Logic error")); |
93 | | |
94 | 13 | ExplodedNode *N = C.generateNonFatalErrorNode(); |
95 | 13 | if (!N) |
96 | 0 | return; |
97 | 13 | 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 | 13 | R->markInteresting(BaseClassRegion); |
101 | 13 | R->addVisitor(std::make_unique<DeleteBugVisitor>()); |
102 | 13 | C.emitReport(std::move(R)); |
103 | 13 | } |
104 | | |
105 | | PathDiagnosticPieceRef |
106 | | DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( |
107 | | const ExplodedNode *N, BugReporterContext &BRC, |
108 | 504 | PathSensitiveBugReport &BR) { |
109 | | // Stop traversal after the first conversion was found on a path. |
110 | 504 | if (Satisfied) |
111 | 294 | return nullptr; |
112 | | |
113 | 210 | const Stmt *S = N->getStmtForDiagnostics(); |
114 | 210 | if (!S) |
115 | 55 | return nullptr; |
116 | | |
117 | 155 | const auto *CastE = dyn_cast<CastExpr>(S); |
118 | 155 | if (!CastE) |
119 | 111 | return nullptr; |
120 | | |
121 | | // Only interested in DerivedToBase implicit casts. |
122 | | // Explicit casts can have different CastKinds. |
123 | 44 | if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { |
124 | 40 | if (ImplCastE->getCastKind() != CK_DerivedToBase) |
125 | 31 | return nullptr; |
126 | 40 | } |
127 | | |
128 | | // Region associated with the current cast expression. |
129 | 13 | const MemRegion *M = N->getSVal(CastE).getAsRegion(); |
130 | 13 | if (!M) |
131 | 0 | return nullptr; |
132 | | |
133 | | // Check if target region was marked as problematic previously. |
134 | 13 | if (!BR.isInteresting(M)) |
135 | 0 | return nullptr; |
136 | | |
137 | | // Stop traversal on this path. |
138 | 13 | Satisfied = true; |
139 | | |
140 | 13 | SmallString<256> Buf; |
141 | 13 | llvm::raw_svector_ostream OS(Buf); |
142 | 13 | OS << "Conversion from derived to base happened here"; |
143 | 13 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
144 | 13 | N->getLocationContext()); |
145 | 13 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); |
146 | 13 | } |
147 | | |
148 | 1 | void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { |
149 | 1 | mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); |
150 | 1 | } |
151 | | |
152 | | bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( |
153 | 2 | const CheckerManager &mgr) { |
154 | 2 | return true; |
155 | 2 | } |