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