1 | //===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// |
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 the Static Analyzer Checker Manager. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
14 | #include "clang/AST/DeclBase.h" |
15 | #include "clang/AST/Stmt.h" |
16 | #include "clang/Analysis/ProgramPoint.h" |
17 | #include "clang/Basic/JsonSupport.h" |
18 | #include "clang/Basic/LLVM.h" |
19 | #include "clang/Driver/DriverDiagnostic.h" |
20 | #include "clang/StaticAnalyzer/Core/Checker.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" |
24 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
25 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
26 | #include "llvm/ADT/SmallVector.h" |
27 | #include "llvm/Support/Casting.h" |
28 | #include "llvm/Support/ErrorHandling.h" |
29 | #include "llvm/Support/FormatVariadic.h" |
30 | #include <cassert> |
31 | #include <optional> |
32 | #include <vector> |
33 | |
34 | using namespace clang; |
35 | using namespace ento; |
36 | |
37 | bool CheckerManager::hasPathSensitiveCheckers() const { |
38 | const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool { |
39 | return (!Callbacks.empty() || ...); |
40 | }; |
41 | return IfAnyAreNonEmpty( |
42 | StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers, |
43 | PostObjCMessageCheckers, PreCallCheckers, PostCallCheckers, |
44 | LocationCheckers, BindCheckers, EndAnalysisCheckers, |
45 | BeginFunctionCheckers, EndFunctionCheckers, BranchConditionCheckers, |
46 | NewAllocatorCheckers, LiveSymbolsCheckers, DeadSymbolsCheckers, |
47 | RegionChangesCheckers, PointerEscapeCheckers, EvalAssumeCheckers, |
48 | EvalCallCheckers, EndOfTranslationUnitCheckers); |
49 | } |
50 | |
51 | void CheckerManager::finishedCheckerRegistration() { |
52 | #ifndef NDEBUG |
53 | // Make sure that for every event that has listeners, there is at least |
54 | // one dispatcher registered for it. |
55 | for (const auto &Event : Events) |
56 | assert(Event.second.HasDispatcher && |
57 | "No dispatcher registered for an event" ); |
58 | #endif |
59 | } |
60 | |
61 | void CheckerManager::reportInvalidCheckerOptionValue( |
62 | const CheckerBase *C, StringRef OptionName, |
63 | StringRef ExpectedValueDesc) const { |
64 | |
65 | getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input) |
66 | << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str() |
67 | << ExpectedValueDesc; |
68 | } |
69 | |
70 | //===----------------------------------------------------------------------===// |
71 | // Functions for running checkers for AST traversing.. |
72 | //===----------------------------------------------------------------------===// |
73 | |
74 | void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, |
75 | BugReporter &BR) { |
76 | assert(D); |
77 | |
78 | unsigned DeclKind = D->getKind(); |
79 | CachedDeclCheckers *checkers = nullptr; |
80 | CachedDeclCheckersMapTy::iterator CCI = CachedDeclCheckersMap.find(Val: DeclKind); |
81 | if (CCI != CachedDeclCheckersMap.end()) { |
82 | checkers = &(CCI->second); |
83 | } else { |
84 | // Find the checkers that should run for this Decl and cache them. |
85 | checkers = &CachedDeclCheckersMap[DeclKind]; |
86 | for (const auto &info : DeclCheckers) |
87 | if (info.IsForDeclFn(D)) |
88 | checkers->push_back(Elt: info.CheckFn); |
89 | } |
90 | |
91 | assert(checkers); |
92 | for (const auto &checker : *checkers) |
93 | checker(D, mgr, BR); |
94 | } |
95 | |
96 | void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, |
97 | BugReporter &BR) { |
98 | assert(D && D->hasBody()); |
99 | |
100 | for (const auto &BodyChecker : BodyCheckers) |
101 | BodyChecker(D, mgr, BR); |
102 | } |
103 | |
104 | //===----------------------------------------------------------------------===// |
105 | // Functions for running checkers for path-sensitive checking. |
106 | //===----------------------------------------------------------------------===// |
107 | |
108 | template <typename CHECK_CTX> |
109 | static void expandGraphWithCheckers(CHECK_CTX checkCtx, |
110 | ExplodedNodeSet &Dst, |
111 | const ExplodedNodeSet &Src) { |
112 | const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext(); |
113 | if (Src.empty()) |
114 | return; |
115 | |
116 | typename CHECK_CTX::CheckersTy::const_iterator |
117 | I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); |
118 | if (I == E) { |
119 | Dst.insert(S: Src); |
120 | return; |
121 | } |
122 | |
123 | ExplodedNodeSet Tmp1, Tmp2; |
124 | const ExplodedNodeSet *PrevSet = &Src; |
125 | |
126 | for (; I != E; ++I) { |
127 | ExplodedNodeSet *CurrSet = nullptr; |
128 | if (I+1 == E) |
129 | CurrSet = &Dst; |
130 | else { |
131 | CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1; |
132 | CurrSet->clear(); |
133 | } |
134 | |
135 | NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); |
136 | for (const auto &NI : *PrevSet) |
137 | checkCtx.runChecker(*I, B, NI); |
138 | |
139 | // If all the produced transitions are sinks, stop. |
140 | if (CurrSet->empty()) |
141 | return; |
142 | |
143 | // Update which NodeSet is the current one. |
144 | PrevSet = CurrSet; |
145 | } |
146 | } |
147 | |
148 | namespace { |
149 | |
150 | struct CheckStmtContext { |
151 | using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>; |
152 | |
153 | bool IsPreVisit; |
154 | const CheckersTy &Checkers; |
155 | const Stmt *S; |
156 | ExprEngine &Eng; |
157 | bool WasInlined; |
158 | |
159 | CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, |
160 | const Stmt *s, ExprEngine &eng, bool wasInlined = false) |
161 | : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), |
162 | WasInlined(wasInlined) {} |
163 | |
164 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
165 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
166 | |
167 | void runChecker(CheckerManager::CheckStmtFunc checkFn, |
168 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
169 | // FIXME: Remove respondsToCallback from CheckerContext; |
170 | ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : |
171 | ProgramPoint::PostStmtKind; |
172 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, |
173 | LC: Pred->getLocationContext(), tag: checkFn.Checker); |
174 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
175 | checkFn(S, C); |
176 | } |
177 | }; |
178 | |
179 | } // namespace |
180 | |
181 | /// Run checkers for visiting Stmts. |
182 | void CheckerManager::runCheckersForStmt(bool isPreVisit, |
183 | ExplodedNodeSet &Dst, |
184 | const ExplodedNodeSet &Src, |
185 | const Stmt *S, |
186 | ExprEngine &Eng, |
187 | bool WasInlined) { |
188 | CheckStmtContext C(isPreVisit, getCachedStmtCheckersFor(S, isPreVisit), |
189 | S, Eng, WasInlined); |
190 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
191 | } |
192 | |
193 | namespace { |
194 | |
195 | struct CheckObjCMessageContext { |
196 | using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>; |
197 | |
198 | ObjCMessageVisitKind Kind; |
199 | bool WasInlined; |
200 | const CheckersTy &Checkers; |
201 | const ObjCMethodCall &Msg; |
202 | ExprEngine &Eng; |
203 | |
204 | CheckObjCMessageContext(ObjCMessageVisitKind visitKind, |
205 | const CheckersTy &checkers, |
206 | const ObjCMethodCall &msg, ExprEngine &eng, |
207 | bool wasInlined) |
208 | : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg), |
209 | Eng(eng) {} |
210 | |
211 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
212 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
213 | |
214 | void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, |
215 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
216 | bool IsPreVisit; |
217 | |
218 | switch (Kind) { |
219 | case ObjCMessageVisitKind::Pre: |
220 | IsPreVisit = true; |
221 | break; |
222 | case ObjCMessageVisitKind::MessageNil: |
223 | case ObjCMessageVisitKind::Post: |
224 | IsPreVisit = false; |
225 | break; |
226 | } |
227 | |
228 | const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,Tag: checkFn.Checker); |
229 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
230 | |
231 | checkFn(*Msg.cloneWithState<ObjCMethodCall>(NewState: Pred->getState()), C); |
232 | } |
233 | }; |
234 | |
235 | } // namespace |
236 | |
237 | /// Run checkers for visiting obj-c messages. |
238 | void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, |
239 | ExplodedNodeSet &Dst, |
240 | const ExplodedNodeSet &Src, |
241 | const ObjCMethodCall &msg, |
242 | ExprEngine &Eng, |
243 | bool WasInlined) { |
244 | const auto &checkers = getObjCMessageCheckers(Kind: visitKind); |
245 | CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined); |
246 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
247 | } |
248 | |
249 | const std::vector<CheckerManager::CheckObjCMessageFunc> & |
250 | CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const { |
251 | switch (Kind) { |
252 | case ObjCMessageVisitKind::Pre: |
253 | return PreObjCMessageCheckers; |
254 | break; |
255 | case ObjCMessageVisitKind::Post: |
256 | return PostObjCMessageCheckers; |
257 | case ObjCMessageVisitKind::MessageNil: |
258 | return ObjCMessageNilCheckers; |
259 | } |
260 | llvm_unreachable("Unknown Kind" ); |
261 | } |
262 | |
263 | namespace { |
264 | |
265 | // FIXME: This has all the same signatures as CheckObjCMessageContext. |
266 | // Is there a way we can merge the two? |
267 | struct CheckCallContext { |
268 | using CheckersTy = std::vector<CheckerManager::CheckCallFunc>; |
269 | |
270 | bool IsPreVisit, WasInlined; |
271 | const CheckersTy &Checkers; |
272 | const CallEvent &Call; |
273 | ExprEngine &Eng; |
274 | |
275 | CheckCallContext(bool isPreVisit, const CheckersTy &checkers, |
276 | const CallEvent &call, ExprEngine &eng, |
277 | bool wasInlined) |
278 | : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), |
279 | Call(call), Eng(eng) {} |
280 | |
281 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
282 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
283 | |
284 | void runChecker(CheckerManager::CheckCallFunc checkFn, |
285 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
286 | const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,Tag: checkFn.Checker); |
287 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
288 | |
289 | checkFn(*Call.cloneWithState(NewState: Pred->getState()), C); |
290 | } |
291 | }; |
292 | |
293 | } // namespace |
294 | |
295 | /// Run checkers for visiting an abstract call event. |
296 | void CheckerManager::runCheckersForCallEvent(bool isPreVisit, |
297 | ExplodedNodeSet &Dst, |
298 | const ExplodedNodeSet &Src, |
299 | const CallEvent &Call, |
300 | ExprEngine &Eng, |
301 | bool WasInlined) { |
302 | CheckCallContext C(isPreVisit, |
303 | isPreVisit ? PreCallCheckers |
304 | : PostCallCheckers, |
305 | Call, Eng, WasInlined); |
306 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
307 | } |
308 | |
309 | namespace { |
310 | |
311 | struct CheckLocationContext { |
312 | using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>; |
313 | |
314 | const CheckersTy &Checkers; |
315 | SVal Loc; |
316 | bool IsLoad; |
317 | const Stmt *NodeEx; /* Will become a CFGStmt */ |
318 | const Stmt *BoundEx; |
319 | ExprEngine &Eng; |
320 | |
321 | CheckLocationContext(const CheckersTy &checkers, |
322 | SVal loc, bool isLoad, const Stmt *NodeEx, |
323 | const Stmt *BoundEx, |
324 | ExprEngine &eng) |
325 | : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), |
326 | BoundEx(BoundEx), Eng(eng) {} |
327 | |
328 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
329 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
330 | |
331 | void runChecker(CheckerManager::CheckLocationFunc checkFn, |
332 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
333 | ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : |
334 | ProgramPoint::PreStoreKind; |
335 | const ProgramPoint &L = |
336 | ProgramPoint::getProgramPoint(S: NodeEx, K, |
337 | LC: Pred->getLocationContext(), |
338 | tag: checkFn.Checker); |
339 | CheckerContext C(Bldr, Eng, Pred, L); |
340 | checkFn(Loc, IsLoad, BoundEx, C); |
341 | } |
342 | }; |
343 | |
344 | } // namespace |
345 | |
346 | /// Run checkers for load/store of a location. |
347 | |
348 | void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, |
349 | const ExplodedNodeSet &Src, |
350 | SVal location, bool isLoad, |
351 | const Stmt *NodeEx, |
352 | const Stmt *BoundEx, |
353 | ExprEngine &Eng) { |
354 | CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx, |
355 | BoundEx, Eng); |
356 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
357 | } |
358 | |
359 | namespace { |
360 | |
361 | struct CheckBindContext { |
362 | using CheckersTy = std::vector<CheckerManager::CheckBindFunc>; |
363 | |
364 | const CheckersTy &Checkers; |
365 | SVal Loc; |
366 | SVal Val; |
367 | const Stmt *S; |
368 | ExprEngine &Eng; |
369 | const ProgramPoint &PP; |
370 | |
371 | CheckBindContext(const CheckersTy &checkers, |
372 | SVal loc, SVal val, const Stmt *s, ExprEngine &eng, |
373 | const ProgramPoint &pp) |
374 | : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {} |
375 | |
376 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
377 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
378 | |
379 | void runChecker(CheckerManager::CheckBindFunc checkFn, |
380 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
381 | const ProgramPoint &L = PP.withTag(tag: checkFn.Checker); |
382 | CheckerContext C(Bldr, Eng, Pred, L); |
383 | |
384 | checkFn(Loc, Val, S, C); |
385 | } |
386 | }; |
387 | |
388 | } // namespace |
389 | |
390 | /// Run checkers for binding of a value to a location. |
391 | void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, |
392 | const ExplodedNodeSet &Src, |
393 | SVal location, SVal val, |
394 | const Stmt *S, ExprEngine &Eng, |
395 | const ProgramPoint &PP) { |
396 | CheckBindContext C(BindCheckers, location, val, S, Eng, PP); |
397 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
398 | } |
399 | |
400 | void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, |
401 | BugReporter &BR, |
402 | ExprEngine &Eng) { |
403 | for (const auto &EndAnalysisChecker : EndAnalysisCheckers) |
404 | EndAnalysisChecker(G, BR, Eng); |
405 | } |
406 | |
407 | namespace { |
408 | |
409 | struct CheckBeginFunctionContext { |
410 | using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>; |
411 | |
412 | const CheckersTy &Checkers; |
413 | ExprEngine &Eng; |
414 | const ProgramPoint &PP; |
415 | |
416 | CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng, |
417 | const ProgramPoint &PP) |
418 | : Checkers(Checkers), Eng(Eng), PP(PP) {} |
419 | |
420 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
421 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
422 | |
423 | void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn, |
424 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
425 | const ProgramPoint &L = PP.withTag(tag: checkFn.Checker); |
426 | CheckerContext C(Bldr, Eng, Pred, L); |
427 | |
428 | checkFn(C); |
429 | } |
430 | }; |
431 | |
432 | } // namespace |
433 | |
434 | void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, |
435 | const BlockEdge &L, |
436 | ExplodedNode *Pred, |
437 | ExprEngine &Eng) { |
438 | ExplodedNodeSet Src; |
439 | Src.insert(S: Pred); |
440 | CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L); |
441 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
442 | } |
443 | |
444 | /// Run checkers for end of path. |
445 | // Note, We do not chain the checker output (like in expandGraphWithCheckers) |
446 | // for this callback since end of path nodes are expected to be final. |
447 | void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, |
448 | ExplodedNodeSet &Dst, |
449 | ExplodedNode *Pred, |
450 | ExprEngine &Eng, |
451 | const ReturnStmt *RS) { |
452 | // We define the builder outside of the loop because if at least one checker |
453 | // creates a successor for Pred, we do not need to generate an |
454 | // autotransition for it. |
455 | NodeBuilder Bldr(Pred, Dst, BC); |
456 | for (const auto &checkFn : EndFunctionCheckers) { |
457 | const ProgramPoint &L = |
458 | FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker); |
459 | CheckerContext C(Bldr, Eng, Pred, L); |
460 | checkFn(RS, C); |
461 | } |
462 | } |
463 | |
464 | namespace { |
465 | |
466 | struct CheckBranchConditionContext { |
467 | using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>; |
468 | |
469 | const CheckersTy &Checkers; |
470 | const Stmt *Condition; |
471 | ExprEngine &Eng; |
472 | |
473 | CheckBranchConditionContext(const CheckersTy &checkers, |
474 | const Stmt *Cond, ExprEngine &eng) |
475 | : Checkers(checkers), Condition(Cond), Eng(eng) {} |
476 | |
477 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
478 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
479 | |
480 | void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, |
481 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
482 | ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(), |
483 | checkFn.Checker); |
484 | CheckerContext C(Bldr, Eng, Pred, L); |
485 | checkFn(Condition, C); |
486 | } |
487 | }; |
488 | |
489 | } // namespace |
490 | |
491 | /// Run checkers for branch condition. |
492 | void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, |
493 | ExplodedNodeSet &Dst, |
494 | ExplodedNode *Pred, |
495 | ExprEngine &Eng) { |
496 | ExplodedNodeSet Src; |
497 | Src.insert(S: Pred); |
498 | CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng); |
499 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
500 | } |
501 | |
502 | namespace { |
503 | |
504 | struct CheckNewAllocatorContext { |
505 | using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>; |
506 | |
507 | const CheckersTy &Checkers; |
508 | const CXXAllocatorCall &Call; |
509 | bool WasInlined; |
510 | ExprEngine &Eng; |
511 | |
512 | CheckNewAllocatorContext(const CheckersTy &Checkers, |
513 | const CXXAllocatorCall &Call, bool WasInlined, |
514 | ExprEngine &Eng) |
515 | : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {} |
516 | |
517 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
518 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
519 | |
520 | void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn, |
521 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
522 | ProgramPoint L = |
523 | PostAllocatorCall(Call.getOriginExpr(), Pred->getLocationContext()); |
524 | CheckerContext C(Bldr, Eng, Pred, L, WasInlined); |
525 | checkFn(cast<CXXAllocatorCall>(Val: *Call.cloneWithState(NewState: Pred->getState())), |
526 | C); |
527 | } |
528 | }; |
529 | |
530 | } // namespace |
531 | |
532 | void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call, |
533 | ExplodedNodeSet &Dst, |
534 | ExplodedNode *Pred, |
535 | ExprEngine &Eng, |
536 | bool WasInlined) { |
537 | ExplodedNodeSet Src; |
538 | Src.insert(S: Pred); |
539 | CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng); |
540 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
541 | } |
542 | |
543 | /// Run checkers for live symbols. |
544 | void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, |
545 | SymbolReaper &SymReaper) { |
546 | for (const auto &LiveSymbolsChecker : LiveSymbolsCheckers) |
547 | LiveSymbolsChecker(state, SymReaper); |
548 | } |
549 | |
550 | namespace { |
551 | |
552 | struct CheckDeadSymbolsContext { |
553 | using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>; |
554 | |
555 | const CheckersTy &Checkers; |
556 | SymbolReaper &SR; |
557 | const Stmt *S; |
558 | ExprEngine &Eng; |
559 | ProgramPoint::Kind ProgarmPointKind; |
560 | |
561 | CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, |
562 | const Stmt *s, ExprEngine &eng, |
563 | ProgramPoint::Kind K) |
564 | : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {} |
565 | |
566 | CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } |
567 | CheckersTy::const_iterator checkers_end() { return Checkers.end(); } |
568 | |
569 | void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, |
570 | NodeBuilder &Bldr, ExplodedNode *Pred) { |
571 | const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K: ProgarmPointKind, |
572 | LC: Pred->getLocationContext(), tag: checkFn.Checker); |
573 | CheckerContext C(Bldr, Eng, Pred, L); |
574 | |
575 | // Note, do not pass the statement to the checkers without letting them |
576 | // differentiate if we ran remove dead bindings before or after the |
577 | // statement. |
578 | checkFn(SR, C); |
579 | } |
580 | }; |
581 | |
582 | } // namespace |
583 | |
584 | /// Run checkers for dead symbols. |
585 | void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, |
586 | const ExplodedNodeSet &Src, |
587 | SymbolReaper &SymReaper, |
588 | const Stmt *S, |
589 | ExprEngine &Eng, |
590 | ProgramPoint::Kind K) { |
591 | CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); |
592 | expandGraphWithCheckers(checkCtx: C, Dst, Src); |
593 | } |
594 | |
595 | /// Run checkers for region changes. |
596 | ProgramStateRef |
597 | CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, |
598 | const InvalidatedSymbols *invalidated, |
599 | ArrayRef<const MemRegion *> ExplicitRegions, |
600 | ArrayRef<const MemRegion *> Regions, |
601 | const LocationContext *LCtx, |
602 | const CallEvent *Call) { |
603 | for (const auto &RegionChangesChecker : RegionChangesCheckers) { |
604 | // If any checker declares the state infeasible (or if it starts that way), |
605 | // bail out. |
606 | if (!state) |
607 | return nullptr; |
608 | state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions, |
609 | LCtx, Call); |
610 | } |
611 | return state; |
612 | } |
613 | |
614 | /// Run checkers to process symbol escape event. |
615 | ProgramStateRef |
616 | CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, |
617 | const InvalidatedSymbols &Escaped, |
618 | const CallEvent *Call, |
619 | PointerEscapeKind Kind, |
620 | RegionAndSymbolInvalidationTraits *ETraits) { |
621 | assert((Call != nullptr || |
622 | (Kind != PSK_DirectEscapeOnCall && |
623 | Kind != PSK_IndirectEscapeOnCall)) && |
624 | "Call must not be NULL when escaping on call" ); |
625 | for (const auto &PointerEscapeChecker : PointerEscapeCheckers) { |
626 | // If any checker declares the state infeasible (or if it starts that |
627 | // way), bail out. |
628 | if (!State) |
629 | return nullptr; |
630 | State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits); |
631 | } |
632 | return State; |
633 | } |
634 | |
635 | /// Run checkers for handling assumptions on symbolic values. |
636 | ProgramStateRef |
637 | CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, |
638 | SVal Cond, bool Assumption) { |
639 | for (const auto &EvalAssumeChecker : EvalAssumeCheckers) { |
640 | // If any checker declares the state infeasible (or if it starts that way), |
641 | // bail out. |
642 | if (!state) |
643 | return nullptr; |
644 | state = EvalAssumeChecker(state, Cond, Assumption); |
645 | } |
646 | return state; |
647 | } |
648 | |
649 | /// Run checkers for evaluating a call. |
650 | /// Only one checker will evaluate the call. |
651 | void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, |
652 | const ExplodedNodeSet &Src, |
653 | const CallEvent &Call, |
654 | ExprEngine &Eng, |
655 | const EvalCallOptions &CallOpts) { |
656 | for (auto *const Pred : Src) { |
657 | std::optional<CheckerNameRef> evaluatorChecker; |
658 | |
659 | ExplodedNodeSet checkDst; |
660 | NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); |
661 | |
662 | // Check if any of the EvalCall callbacks can evaluate the call. |
663 | for (const auto &EvalCallChecker : EvalCallCheckers) { |
664 | // TODO: Support the situation when the call doesn't correspond |
665 | // to any Expr. |
666 | ProgramPoint L = ProgramPoint::getProgramPoint( |
667 | Call.getOriginExpr(), ProgramPoint::PostStmtKind, |
668 | Pred->getLocationContext(), EvalCallChecker.Checker); |
669 | bool evaluated = false; |
670 | { // CheckerContext generates transitions(populates checkDest) on |
671 | // destruction, so introduce the scope to make sure it gets properly |
672 | // populated. |
673 | CheckerContext C(B, Eng, Pred, L); |
674 | evaluated = EvalCallChecker(Call, C); |
675 | } |
676 | #ifndef NDEBUG |
677 | if (evaluated && evaluatorChecker) { |
678 | const auto toString = [](const CallEvent &Call) -> std::string { |
679 | std::string Buf; |
680 | llvm::raw_string_ostream OS(Buf); |
681 | Call.dump(Out&: OS); |
682 | OS.flush(); |
683 | return Buf; |
684 | }; |
685 | std::string AssertionMessage = llvm::formatv( |
686 | Fmt: "The '{0}' call has been already evaluated by the {1} checker, " |
687 | "while the {2} checker also tried to evaluate the same call. At " |
688 | "most one checker supposed to evaluate a call." , |
689 | Vals: toString(Call), Vals: evaluatorChecker->getName(), |
690 | Vals: EvalCallChecker.Checker->getCheckerName()); |
691 | llvm_unreachable(AssertionMessage.c_str()); |
692 | } |
693 | #endif |
694 | if (evaluated) { |
695 | evaluatorChecker = EvalCallChecker.Checker->getCheckerName(); |
696 | Dst.insert(S: checkDst); |
697 | #ifdef NDEBUG |
698 | break; // on release don't check that no other checker also evals. |
699 | #endif |
700 | } |
701 | } |
702 | |
703 | // If none of the checkers evaluated the call, ask ExprEngine to handle it. |
704 | if (!evaluatorChecker) { |
705 | NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); |
706 | Eng.defaultEvalCall(B, Pred, Call, CallOpts); |
707 | } |
708 | } |
709 | } |
710 | |
711 | /// Run checkers for the entire Translation Unit. |
712 | void CheckerManager::runCheckersOnEndOfTranslationUnit( |
713 | const TranslationUnitDecl *TU, |
714 | AnalysisManager &mgr, |
715 | BugReporter &BR) { |
716 | for (const auto &EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers) |
717 | EndOfTranslationUnitChecker(TU, mgr, BR); |
718 | } |
719 | |
720 | void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, |
721 | ProgramStateRef State, |
722 | const char *NL, |
723 | unsigned int Space, |
724 | bool IsDot) const { |
725 | Indent(Out, Space, IsDot) << "\"checker_messages\": " ; |
726 | |
727 | // Create a temporary stream to see whether we have any message. |
728 | SmallString<1024> TempBuf; |
729 | llvm::raw_svector_ostream TempOut(TempBuf); |
730 | unsigned int InnerSpace = Space + 2; |
731 | |
732 | // Create the new-line in JSON with enough space. |
733 | SmallString<128> NewLine; |
734 | llvm::raw_svector_ostream NLOut(NewLine); |
735 | NLOut << "\", " << NL; // Inject the ending and a new line |
736 | Indent(Out&: NLOut, Space: InnerSpace, IsDot) << "\"" ; // then begin the next message. |
737 | |
738 | ++Space; |
739 | bool HasMessage = false; |
740 | |
741 | // Store the last CheckerTag. |
742 | const void *LastCT = nullptr; |
743 | for (const auto &CT : CheckerTags) { |
744 | // See whether the current checker has a message. |
745 | CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"" ); |
746 | |
747 | if (TempBuf.empty()) |
748 | continue; |
749 | |
750 | if (!HasMessage) { |
751 | Out << '[' << NL; |
752 | HasMessage = true; |
753 | } |
754 | |
755 | LastCT = &CT; |
756 | TempBuf.clear(); |
757 | } |
758 | |
759 | for (const auto &CT : CheckerTags) { |
760 | // See whether the current checker has a message. |
761 | CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"" ); |
762 | |
763 | if (TempBuf.empty()) |
764 | continue; |
765 | |
766 | Indent(Out, Space, IsDot) |
767 | << "{ \"checker\": \"" << CT.second->getCheckerName().getName() |
768 | << "\", \"messages\": [" << NL; |
769 | Indent(Out, Space: InnerSpace, IsDot) |
770 | << '\"' << TempBuf.str().trim() << '\"' << NL; |
771 | Indent(Out, Space, IsDot) << "]}" ; |
772 | |
773 | if (&CT != LastCT) |
774 | Out << ','; |
775 | Out << NL; |
776 | |
777 | TempBuf.clear(); |
778 | } |
779 | |
780 | // It is the last element of the 'program_state' so do not add a comma. |
781 | if (HasMessage) |
782 | Indent(Out, Space: --Space, IsDot) << "]" ; |
783 | else |
784 | Out << "null" ; |
785 | |
786 | Out << NL; |
787 | } |
788 | |
789 | //===----------------------------------------------------------------------===// |
790 | // Internal registration functions for AST traversing. |
791 | //===----------------------------------------------------------------------===// |
792 | |
793 | void CheckerManager::_registerForDecl(CheckDeclFunc checkfn, |
794 | HandlesDeclFunc isForDeclFn) { |
795 | DeclCheckerInfo info = { .CheckFn: checkfn, .IsForDeclFn: isForDeclFn }; |
796 | DeclCheckers.push_back(x: info); |
797 | } |
798 | |
799 | void CheckerManager::_registerForBody(CheckDeclFunc checkfn) { |
800 | BodyCheckers.push_back(x: checkfn); |
801 | } |
802 | |
803 | //===----------------------------------------------------------------------===// |
804 | // Internal registration functions for path-sensitive checking. |
805 | //===----------------------------------------------------------------------===// |
806 | |
807 | void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, |
808 | HandlesStmtFunc isForStmtFn) { |
809 | StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/true }; |
810 | StmtCheckers.push_back(x: info); |
811 | } |
812 | |
813 | void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, |
814 | HandlesStmtFunc isForStmtFn) { |
815 | StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/false }; |
816 | StmtCheckers.push_back(x: info); |
817 | } |
818 | |
819 | void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { |
820 | PreObjCMessageCheckers.push_back(x: checkfn); |
821 | } |
822 | |
823 | void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) { |
824 | ObjCMessageNilCheckers.push_back(x: checkfn); |
825 | } |
826 | |
827 | void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { |
828 | PostObjCMessageCheckers.push_back(x: checkfn); |
829 | } |
830 | |
831 | void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) { |
832 | PreCallCheckers.push_back(x: checkfn); |
833 | } |
834 | void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) { |
835 | PostCallCheckers.push_back(x: checkfn); |
836 | } |
837 | |
838 | void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { |
839 | LocationCheckers.push_back(x: checkfn); |
840 | } |
841 | |
842 | void CheckerManager::_registerForBind(CheckBindFunc checkfn) { |
843 | BindCheckers.push_back(x: checkfn); |
844 | } |
845 | |
846 | void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { |
847 | EndAnalysisCheckers.push_back(x: checkfn); |
848 | } |
849 | |
850 | void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) { |
851 | BeginFunctionCheckers.push_back(x: checkfn); |
852 | } |
853 | |
854 | void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { |
855 | EndFunctionCheckers.push_back(x: checkfn); |
856 | } |
857 | |
858 | void CheckerManager::_registerForBranchCondition( |
859 | CheckBranchConditionFunc checkfn) { |
860 | BranchConditionCheckers.push_back(x: checkfn); |
861 | } |
862 | |
863 | void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) { |
864 | NewAllocatorCheckers.push_back(x: checkfn); |
865 | } |
866 | |
867 | void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) { |
868 | LiveSymbolsCheckers.push_back(x: checkfn); |
869 | } |
870 | |
871 | void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) { |
872 | DeadSymbolsCheckers.push_back(x: checkfn); |
873 | } |
874 | |
875 | void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) { |
876 | RegionChangesCheckers.push_back(x: checkfn); |
877 | } |
878 | |
879 | void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ |
880 | PointerEscapeCheckers.push_back(x: checkfn); |
881 | } |
882 | |
883 | void CheckerManager::_registerForConstPointerEscape( |
884 | CheckPointerEscapeFunc checkfn) { |
885 | PointerEscapeCheckers.push_back(x: checkfn); |
886 | } |
887 | |
888 | void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { |
889 | EvalAssumeCheckers.push_back(x: checkfn); |
890 | } |
891 | |
892 | void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { |
893 | EvalCallCheckers.push_back(x: checkfn); |
894 | } |
895 | |
896 | void CheckerManager::_registerForEndOfTranslationUnit( |
897 | CheckEndOfTranslationUnit checkfn) { |
898 | EndOfTranslationUnitCheckers.push_back(x: checkfn); |
899 | } |
900 | |
901 | //===----------------------------------------------------------------------===// |
902 | // Implementation details. |
903 | //===----------------------------------------------------------------------===// |
904 | |
905 | const CheckerManager::CachedStmtCheckers & |
906 | CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { |
907 | assert(S); |
908 | |
909 | unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit); |
910 | CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(Val: Key); |
911 | if (CCI != CachedStmtCheckersMap.end()) |
912 | return CCI->second; |
913 | |
914 | // Find the checkers that should run for this Stmt and cache them. |
915 | CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key]; |
916 | for (const auto &Info : StmtCheckers) |
917 | if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S)) |
918 | Checkers.push_back(Elt: Info.CheckFn); |
919 | return Checkers; |
920 | } |
921 | |