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
539/// Run checkers for end of a function (either the entrypoint or another
540/// function that was inlined). Note that this function places the
541/// checker activations on separate execution paths:
542/// /-[checker1]-> N1 ...
543/// Pred --[checker2]-> N2 ...
544/// \-[checker3]-> N3 ...
545/// (If none of the checkers produce a transition, we continue with 'Pred'.)
546///
547/// This differs from the handling of all the other checker callbacks, where
548/// the checker activations are chained sequentially on a single path:
549/// Pred --[checker1]-> N1 --[checker2]-> N2 --[checker3]-> N3 ...
550///
551/// This difference has historical reasons: originally this callback was called
552/// 'EndPath' and only activated at the end of an execution paths, and
553/// (according to an old comment) those 'EndPath' checkers expected that they
554/// create an "end of path" node which will be final.
555/// TODO: Check whether this exceptional behavior is still justified.
556void CheckerManager::runCheckersForEndFunction(ExplodedNodeSet &Dst,
557 ExplodedNode *Pred,
558 ExprEngine &Eng,
559 const ReturnStmt *RS) {
560 // By default, continue from 'Pred' -- this will be removed from 'Dst' if any
561 // checker generates a transition from it.
562 Dst.insert(N: Pred);
563
564 for (const auto &checkFn : EndFunctionCheckers) {
565 const ProgramPoint &L =
566 FunctionExitPoint(RS, Pred->getStackFrame(), checkFn.Checker);
567 CheckerContext C(Eng, Pred, Dst, L);
568 llvm::TimeTraceScope TimeScope(checkerScopeName(Name: "End", Checker: checkFn.Checker));
569 checkFn(RS, C);
570 }
571}
572
573namespace {
574
575 struct CheckBranchConditionContext {
576 using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>;
577
578 const CheckersTy &Checkers;
579 const Stmt *Condition;
580 ExprEngine &Eng;
581
582 CheckBranchConditionContext(const CheckersTy &checkers,
583 const Stmt *Cond, ExprEngine &eng)
584 : Checkers(checkers), Condition(Cond), Eng(eng) {}
585
586 CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
587 CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
588
589 void runChecker(CheckerManager::CheckBranchConditionFunc checkFn,
590 ExplodedNode *Pred, ExplodedNodeSet &Dst) {
591 llvm::TimeTraceScope TimeScope(
592 checkerScopeName(Name: "BranchCond", Checker: checkFn.Checker));
593 ProgramPoint L =
594 PostCondition(Condition, Pred->getStackFrame(), checkFn.Checker);
595 CheckerContext C(Eng, Pred, Dst, L);
596 checkFn(Condition, C);
597 }
598 };
599
600} // namespace
601
602/// Run checkers for branch condition.
603void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
604 ExplodedNodeSet &Dst,
605 ExplodedNode *Pred,
606 ExprEngine &Eng) {
607 ExplodedNodeSet Src;
608 Src.insert(N: Pred);
609 CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng);
610 llvm::TimeTraceScope TimeScope(
611 "CheckerManager::runCheckersForBranchCondition");
612 expandGraphWithCheckers(checkCtx: C, Dst, Src);
613}
614
615namespace {
616
617 struct CheckNewAllocatorContext {
618 using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
619
620 const CheckersTy &Checkers;
621 const CXXAllocatorCall &Call;
622 bool WasInlined;
623 ExprEngine &Eng;
624
625 CheckNewAllocatorContext(const CheckersTy &Checkers,
626 const CXXAllocatorCall &Call, bool WasInlined,
627 ExprEngine &Eng)
628 : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {}
629
630 CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
631 CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
632
633 void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
634 ExplodedNode *Pred, ExplodedNodeSet &Dst) {
635 llvm::TimeTraceScope TimeScope(
636 checkerScopeName(Name: "Allocator", Checker: checkFn.Checker));
637 ProgramPoint L = PostAllocatorCall(
638 Call.getOriginExpr(), Pred->getStackFrame(), checkFn.Checker);
639 CheckerContext C(Eng, Pred, Dst, L, WasInlined);
640 checkFn(cast<CXXAllocatorCall>(Val: *Call.cloneWithState(NewState: Pred->getState())),
641 C);
642 }
643 };
644
645} // namespace
646
647void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call,
648 ExplodedNodeSet &Dst,
649 ExplodedNode *Pred,
650 ExprEngine &Eng,
651 bool WasInlined) {
652 ExplodedNodeSet Src;
653 Src.insert(N: Pred);
654 CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng);
655 llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForNewAllocator");
656 expandGraphWithCheckers(checkCtx: C, Dst, Src);
657}
658
659/// Run checkers for live symbols.
660void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
661 SymbolReaper &SymReaper) {
662 for (const auto &LiveSymbolsChecker : LiveSymbolsCheckers)
663 LiveSymbolsChecker(state, SymReaper);
664}
665
666namespace {
667
668 struct CheckDeadSymbolsContext {
669 using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>;
670
671 const CheckersTy &Checkers;
672 SymbolReaper &SR;
673 const Stmt *S;
674 ExprEngine &Eng;
675 ProgramPoint::Kind ProgramPointKind;
676
677 CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
678 const Stmt *s, ExprEngine &eng,
679 ProgramPoint::Kind K)
680 : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgramPointKind(K) {}
681
682 CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
683 CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
684
685 void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
686 ExplodedNode *Pred, ExplodedNodeSet &Dst) {
687 llvm::TimeTraceScope TimeScope(
688 checkerScopeName(Name: "DeadSymbols", Checker: checkFn.Checker));
689 const ProgramPoint &L = ProgramPoint::getProgramPoint(
690 S, K: ProgramPointKind, SF: Pred->getStackFrame(), tag: checkFn.Checker);
691 CheckerContext C(Eng, Pred, Dst, L);
692
693 // Note, do not pass the statement to the checkers without letting them
694 // differentiate if we ran remove dead bindings before or after the
695 // statement.
696 checkFn(SR, C);
697 }
698 };
699
700} // namespace
701
702/// Run checkers for dead symbols.
703void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
704 const ExplodedNodeSet &Src,
705 SymbolReaper &SymReaper,
706 const Stmt *S,
707 ExprEngine &Eng,
708 ProgramPoint::Kind K) {
709 CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
710 llvm::TimeTraceScope TimeScope("CheckerManager::runCheckersForDeadSymbols");
711 expandGraphWithCheckers(checkCtx: C, Dst, Src);
712}
713
714/// Run checkers for region changes.
715ProgramStateRef CheckerManager::runCheckersForRegionChanges(
716 ProgramStateRef state, const InvalidatedSymbols *invalidated,
717 ArrayRef<const MemRegion *> ExplicitRegions,
718 ArrayRef<const MemRegion *> Regions, const StackFrame *SF,
719 const CallEvent *Call) {
720 for (const auto &RegionChangesChecker : RegionChangesCheckers) {
721 // If any checker declares the state infeasible (or if it starts that way),
722 // bail out.
723 if (!state)
724 return nullptr;
725 state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions,
726 SF, Call);
727 }
728 return state;
729}
730
731/// Run checkers to process symbol escape event.
732ProgramStateRef
733CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
734 const InvalidatedSymbols &Escaped,
735 const CallEvent *Call,
736 PointerEscapeKind Kind,
737 RegionAndSymbolInvalidationTraits *ETraits) {
738 assert((Call != nullptr ||
739 (Kind != PSK_DirectEscapeOnCall &&
740 Kind != PSK_IndirectEscapeOnCall)) &&
741 "Call must not be NULL when escaping on call");
742 for (const auto &PointerEscapeChecker : PointerEscapeCheckers) {
743 // If any checker declares the state infeasible (or if it starts that
744 // way), bail out.
745 if (!State)
746 return nullptr;
747 State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits);
748 }
749 return State;
750}
751
752/// Run checkers for handling assumptions on symbolic values.
753ProgramStateRef
754CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
755 SVal Cond, bool Assumption) {
756 for (const auto &EvalAssumeChecker : EvalAssumeCheckers) {
757 // If any checker declares the state infeasible (or if it starts that way),
758 // bail out.
759 if (!state)
760 return nullptr;
761 state = EvalAssumeChecker(state, Cond, Assumption);
762 }
763 return state;
764}
765
766/// Run checkers for evaluating a call.
767/// Only one checker will evaluate the call.
768void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
769 const ExplodedNodeSet &Src,
770 const CallEvent &Call,
771 ExprEngine &Eng,
772 const EvalCallOptions &CallOpts) {
773 for (auto *const Pred : Src) {
774 std::optional<StringRef> evaluatorChecker;
775
776 ExplodedNodeSet checkDst{Pred};
777
778 ProgramStateRef State = Pred->getState();
779 CallEventRef<> UpdatedCall = Call.cloneWithState(NewState: State);
780
781 // Check if any of the EvalCall callbacks can evaluate the call.
782 for (const auto &EvalCallChecker : EvalCallCheckers) {
783 // TODO: Support the situation when the call doesn't correspond
784 // to any Expr.
785 ProgramPoint L = ProgramPoint::getProgramPoint(
786 S: UpdatedCall->getOriginExpr(), K: ProgramPoint::PostStmtKind,
787 SF: Pred->getStackFrame(), tag: EvalCallChecker.Checker);
788
789 CheckerContext C(Eng, Pred, checkDst, L);
790 bool evaluated = EvalCallChecker(*UpdatedCall, C);
791#ifndef NDEBUG
792 if (evaluated && evaluatorChecker) {
793 const auto toString = [](const CallEvent &Call) -> std::string {
794 std::string Buf;
795 llvm::raw_string_ostream OS(Buf);
796 Call.dump(OS);
797 return Buf;
798 };
799 std::string AssertionMessage = llvm::formatv(
800 "The '{0}' call has been already evaluated by the {1} checker, "
801 "while the {2} checker also tried to evaluate the same call. At "
802 "most one checker supposed to evaluate a call.",
803 toString(Call), evaluatorChecker,
804 EvalCallChecker.Checker->getDebugTag());
805 llvm_unreachable(AssertionMessage.c_str());
806 }
807#endif
808 if (evaluated) {
809 evaluatorChecker = EvalCallChecker.Checker->getDebugTag();
810 Dst.insert(S: checkDst);
811#ifdef NDEBUG
812 break; // on release don't check that no other checker also evals.
813#endif
814 }
815 }
816
817 // If none of the checkers evaluated the call, ask ExprEngine to handle it.
818 if (!evaluatorChecker)
819 Eng.defaultEvalCall(Dst, Pred, Call: *UpdatedCall, CallOpts);
820 }
821}
822
823/// Run checkers for the entire Translation Unit.
824void CheckerManager::runCheckersOnEndOfTranslationUnit(
825 const TranslationUnitDecl *TU,
826 AnalysisManager &mgr,
827 BugReporter &BR) {
828 for (const auto &EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
829 EndOfTranslationUnitChecker(TU, mgr, BR);
830}
831
832void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out,
833 ProgramStateRef State,
834 const char *NL,
835 unsigned int Space,
836 bool IsDot) const {
837 Indent(Out, Space, IsDot) << "\"checker_messages\": ";
838
839 // Create a temporary stream to see whether we have any message.
840 SmallString<1024> TempBuf;
841 llvm::raw_svector_ostream TempOut(TempBuf);
842 unsigned int InnerSpace = Space + 2;
843
844 // Create the new-line in JSON with enough space.
845 SmallString<128> NewLine;
846 llvm::raw_svector_ostream NLOut(NewLine);
847 NLOut << "\", " << NL; // Inject the ending and a new line
848 Indent(Out&: NLOut, Space: InnerSpace, IsDot) << "\""; // then begin the next message.
849
850 ++Space;
851 bool HasMessage = false;
852
853 // Store the last CheckerTag.
854 const void *LastCT = nullptr;
855 for (const auto &CT : CheckerTags) {
856 // See whether the current checker has a message.
857 CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
858
859 if (TempBuf.empty())
860 continue;
861
862 if (!HasMessage) {
863 Out << '[' << NL;
864 HasMessage = true;
865 }
866
867 LastCT = &CT;
868 TempBuf.clear();
869 }
870
871 for (const auto &CT : CheckerTags) {
872 // See whether the current checker has a message.
873 CT.second->printState(Out&: TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
874
875 if (TempBuf.empty())
876 continue;
877
878 Indent(Out, Space, IsDot) << "{ \"checker\": \"" << CT.second->getDebugTag()
879 << "\", \"messages\": [" << NL;
880 Indent(Out, Space: InnerSpace, IsDot)
881 << '\"' << TempBuf.str().trim() << '\"' << NL;
882 Indent(Out, Space, IsDot) << "]}";
883
884 if (&CT != LastCT)
885 Out << ',';
886 Out << NL;
887
888 TempBuf.clear();
889 }
890
891 // It is the last element of the 'program_state' so do not add a comma.
892 if (HasMessage)
893 Indent(Out, Space: --Space, IsDot) << "]";
894 else
895 Out << "null";
896
897 Out << NL;
898}
899
900//===----------------------------------------------------------------------===//
901// Internal registration functions for AST traversing.
902//===----------------------------------------------------------------------===//
903
904void CheckerManager::_registerForDecl(CheckDeclFunc checkfn,
905 HandlesDeclFunc isForDeclFn) {
906 DeclCheckerInfo info = { .CheckFn: checkfn, .IsForDeclFn: isForDeclFn };
907 DeclCheckers.push_back(x: info);
908}
909
910void CheckerManager::_registerForBody(CheckDeclFunc checkfn) {
911 BodyCheckers.push_back(x: checkfn);
912}
913
914//===----------------------------------------------------------------------===//
915// Internal registration functions for path-sensitive checking.
916//===----------------------------------------------------------------------===//
917
918void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn,
919 HandlesStmtFunc isForStmtFn) {
920 StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/true };
921 StmtCheckers.push_back(x: info);
922}
923
924void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn,
925 HandlesStmtFunc isForStmtFn) {
926 StmtCheckerInfo info = { .CheckFn: checkfn, .IsForStmtFn: isForStmtFn, /*IsPreVisit*/false };
927 StmtCheckers.push_back(x: info);
928}
929
930void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) {
931 PreObjCMessageCheckers.push_back(x: checkfn);
932}
933
934void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) {
935 ObjCMessageNilCheckers.push_back(x: checkfn);
936}
937
938void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
939 PostObjCMessageCheckers.push_back(x: checkfn);
940}
941
942void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
943 PreCallCheckers.push_back(x: checkfn);
944}
945void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
946 PostCallCheckers.push_back(x: checkfn);
947}
948
949void CheckerManager::_registerForLifetimeEnd(CheckLifetimeEndFunc checkfn) {
950 LifetimeEndCheckers.push_back(x: checkfn);
951}
952
953void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
954 LocationCheckers.push_back(x: checkfn);
955}
956
957void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
958 BindCheckers.push_back(x: checkfn);
959}
960
961void CheckerManager::_registerForBlockEntrance(CheckBlockEntranceFunc checkfn) {
962 BlockEntranceCheckers.push_back(x: checkfn);
963}
964
965void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
966 EndAnalysisCheckers.push_back(x: checkfn);
967}
968
969void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) {
970 BeginFunctionCheckers.push_back(x: checkfn);
971}
972
973void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) {
974 EndFunctionCheckers.push_back(x: checkfn);
975}
976
977void CheckerManager::_registerForBranchCondition(
978 CheckBranchConditionFunc checkfn) {
979 BranchConditionCheckers.push_back(x: checkfn);
980}
981
982void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
983 NewAllocatorCheckers.push_back(x: checkfn);
984}
985
986void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
987 LiveSymbolsCheckers.push_back(x: checkfn);
988}
989
990void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) {
991 DeadSymbolsCheckers.push_back(x: checkfn);
992}
993
994void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) {
995 RegionChangesCheckers.push_back(x: checkfn);
996}
997
998void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){
999 PointerEscapeCheckers.push_back(x: checkfn);
1000}
1001
1002void CheckerManager::_registerForConstPointerEscape(
1003 CheckPointerEscapeFunc checkfn) {
1004 PointerEscapeCheckers.push_back(x: checkfn);
1005}
1006
1007void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) {
1008 EvalAssumeCheckers.push_back(x: checkfn);
1009}
1010
1011void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
1012 EvalCallCheckers.push_back(x: checkfn);
1013}
1014
1015void CheckerManager::_registerForEndOfTranslationUnit(
1016 CheckEndOfTranslationUnit checkfn) {
1017 EndOfTranslationUnitCheckers.push_back(x: checkfn);
1018}
1019
1020//===----------------------------------------------------------------------===//
1021// Implementation details.
1022//===----------------------------------------------------------------------===//
1023
1024const CheckerManager::CachedStmtCheckers &
1025CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
1026 assert(S);
1027
1028 unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit);
1029 auto [CCI, Inserted] = CachedStmtCheckersMap.try_emplace(Key);
1030 CachedStmtCheckers &Checkers = CCI->second;
1031 if (Inserted) {
1032 // Find the checkers that should run for this Stmt and cache them.
1033 for (const auto &Info : StmtCheckers)
1034 if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S))
1035 Checkers.push_back(Elt: Info.CheckFn);
1036 }
1037 return Checkers;
1038}
1039