1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | #include "clang/AST/DeclCXX.h" |
16 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
17 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
18 | #include "clang/StaticAnalyzer/Core/Checker.h" |
19 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
20 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" |
23 | |
24 | using namespace clang; |
25 | using namespace ento; |
26 | |
27 | namespace { |
28 | enum class ObjectState : bool { CtorCalled, DtorCalled }; |
29 | } |
30 | |
31 | |
32 | namespace llvm { |
33 | template <> struct FoldingSetTrait<ObjectState> { |
34 | static inline void Profile(ObjectState X, FoldingSetNodeID &ID) { |
35 | ID.AddInteger(static_cast<int>(X)); |
36 | } |
37 | }; |
38 | } |
39 | |
40 | namespace { |
41 | class VirtualCallChecker |
42 | : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { |
43 | mutable std::unique_ptr<BugType> BT; |
44 | |
45 | public: |
46 | |
47 | DefaultBool IsPureOnly; |
48 | |
49 | void checkBeginFunction(CheckerContext &C) const; |
50 | void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; |
51 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
52 | |
53 | private: |
54 | void registerCtorDtorCallInState(bool IsBeginFunction, |
55 | CheckerContext &C) const; |
56 | void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg, |
57 | CheckerContext &C) const; |
58 | |
59 | class VirtualBugVisitor : public BugReporterVisitor { |
60 | private: |
61 | const MemRegion *ObjectRegion; |
62 | bool Found; |
63 | |
64 | public: |
65 | VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {} |
66 | |
67 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
68 | static int X = 0; |
69 | ID.AddPointer(&X); |
70 | ID.AddPointer(ObjectRegion); |
71 | } |
72 | |
73 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
74 | BugReporterContext &BRC, |
75 | BugReport &BR) override; |
76 | }; |
77 | }; |
78 | } |
79 | |
80 | |
81 | REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) |
82 | |
83 | std::shared_ptr<PathDiagnosticPiece> |
84 | VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N, |
85 | BugReporterContext &BRC, |
86 | BugReport &) { |
87 | |
88 | |
89 | if (Found) |
90 | return nullptr; |
91 | |
92 | ProgramStateRef State = N->getState(); |
93 | const LocationContext *LCtx = N->getLocationContext(); |
94 | const CXXConstructorDecl *CD = |
95 | dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl()); |
96 | const CXXDestructorDecl *DD = |
97 | dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl()); |
98 | |
99 | if (!CD && !DD) |
100 | return nullptr; |
101 | |
102 | ProgramStateManager &PSM = State->getStateManager(); |
103 | auto &SVB = PSM.getSValBuilder(); |
104 | const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); |
105 | if (!MD) |
106 | return nullptr; |
107 | auto ThiSVal = |
108 | State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); |
109 | const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion(); |
110 | if (!Reg) |
111 | return nullptr; |
112 | if (Reg != ObjectRegion) |
113 | return nullptr; |
114 | |
115 | const Stmt *S = PathDiagnosticLocation::getStmt(N); |
116 | if (!S) |
117 | return nullptr; |
118 | Found = true; |
119 | |
120 | std::string InfoText; |
121 | if (CD) |
122 | InfoText = "This constructor of an object of type '" + |
123 | CD->getNameAsString() + |
124 | "' has not returned when the virtual method was called"; |
125 | else |
126 | InfoText = "This destructor of an object of type '" + |
127 | DD->getNameAsString() + |
128 | "' has not returned when the virtual method was called"; |
129 | |
130 | |
131 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
132 | N->getLocationContext()); |
133 | return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); |
134 | } |
135 | |
136 | |
137 | static bool isVirtualCall(const CallExpr *CE) { |
138 | bool CallIsNonVirtual = false; |
139 | |
140 | if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { |
141 | |
142 | |
143 | if (CME->getQualifier()) |
144 | CallIsNonVirtual = true; |
145 | |
146 | if (const Expr *Base = CME->getBase()) { |
147 | |
148 | if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>()) |
149 | CallIsNonVirtual = true; |
150 | } |
151 | } |
152 | |
153 | const CXXMethodDecl *MD = |
154 | dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); |
155 | if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() && |
156 | !MD->getParent()->hasAttr<FinalAttr>()) |
157 | return true; |
158 | return false; |
159 | } |
160 | |
161 | |
162 | void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { |
163 | registerCtorDtorCallInState(true, C); |
164 | } |
165 | |
166 | |
167 | void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS, |
168 | CheckerContext &C) const { |
169 | registerCtorDtorCallInState(false, C); |
170 | } |
171 | |
172 | void VirtualCallChecker::checkPreCall(const CallEvent &Call, |
173 | CheckerContext &C) const { |
174 | const auto MC = dyn_cast<CXXMemberCall>(&Call); |
175 | if (!MC) |
176 | return; |
177 | |
178 | const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); |
179 | if (!MD) |
180 | return; |
181 | ProgramStateRef State = C.getState(); |
182 | const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); |
183 | |
184 | if (IsPureOnly && !MD->isPure()) |
185 | return; |
186 | if (!isVirtualCall(CE)) |
187 | return; |
188 | |
189 | const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); |
190 | const ObjectState *ObState = State->get<CtorDtorMap>(Reg); |
191 | if (!ObState) |
192 | return; |
193 | |
194 | |
195 | if (*ObState == ObjectState::CtorCalled) { |
196 | if (IsPureOnly && MD->isPure()) |
197 | reportBug("Call to pure virtual function during construction", true, Reg, |
198 | C); |
199 | else if (!MD->isPure()) |
200 | reportBug("Call to virtual function during construction", false, Reg, C); |
201 | else |
202 | reportBug("Call to pure virtual function during construction", false, Reg, |
203 | C); |
204 | } |
205 | |
206 | if (*ObState == ObjectState::DtorCalled) { |
207 | if (IsPureOnly && MD->isPure()) |
208 | reportBug("Call to pure virtual function during destruction", true, Reg, |
209 | C); |
210 | else if (!MD->isPure()) |
211 | reportBug("Call to virtual function during destruction", false, Reg, C); |
212 | else |
213 | reportBug("Call to pure virtual function during construction", false, Reg, |
214 | C); |
215 | } |
216 | } |
217 | |
218 | void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, |
219 | CheckerContext &C) const { |
220 | const auto *LCtx = C.getLocationContext(); |
221 | const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); |
222 | if (!MD) |
223 | return; |
224 | |
225 | ProgramStateRef State = C.getState(); |
226 | auto &SVB = C.getSValBuilder(); |
227 | |
228 | |
229 | if (isa<CXXConstructorDecl>(MD)) { |
230 | auto ThiSVal = |
231 | State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); |
232 | const MemRegion *Reg = ThiSVal.getAsRegion(); |
233 | if (IsBeginFunction) |
234 | State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); |
235 | else |
236 | State = State->remove<CtorDtorMap>(Reg); |
237 | |
238 | C.addTransition(State); |
239 | return; |
240 | } |
241 | |
242 | |
243 | if (isa<CXXDestructorDecl>(MD)) { |
244 | auto ThiSVal = |
245 | State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); |
246 | const MemRegion *Reg = ThiSVal.getAsRegion(); |
247 | if (IsBeginFunction) |
248 | State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); |
249 | else |
250 | State = State->remove<CtorDtorMap>(Reg); |
251 | |
252 | C.addTransition(State); |
253 | return; |
254 | } |
255 | } |
256 | |
257 | void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink, |
258 | const MemRegion *Reg, |
259 | CheckerContext &C) const { |
260 | ExplodedNode *N; |
261 | if (IsSink) |
262 | N = C.generateErrorNode(); |
263 | else |
264 | N = C.generateNonFatalErrorNode(); |
265 | |
266 | if (!N) |
267 | return; |
268 | if (!BT) |
269 | BT.reset(new BugType( |
270 | this, "Call to virtual function during construction or destruction", |
271 | "C++ Object Lifecycle")); |
272 | |
273 | auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N); |
274 | Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg)); |
275 | C.emitReport(std::move(Reporter)); |
276 | } |
277 | |
278 | void ento::registerVirtualCallChecker(CheckerManager &mgr) { |
279 | VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); |
280 | |
281 | checker->IsPureOnly = |
282 | mgr.getAnalyzerOptions().getCheckerBooleanOption( |
283 | checker, "PureOnly", false); |
284 | } |
285 | |
286 | bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { |
287 | return true; |
288 | } |
289 | |