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