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