Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | //== CheckerContext.h - Context info for path-sensitive checkers--*- C++ -*--=// |
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 | // This file defines CheckerContext that provides contextual info for |
||
10 | // path-sensitive checkers. |
||
11 | // |
||
12 | //===----------------------------------------------------------------------===// |
||
13 | |||
14 | #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H |
||
15 | #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_CHECKERCONTEXT_H |
||
16 | |||
17 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
||
18 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
||
19 | #include <optional> |
||
20 | |||
21 | namespace clang { |
||
22 | namespace ento { |
||
23 | |||
24 | class CheckerContext { |
||
25 | ExprEngine &Eng; |
||
26 | /// The current exploded(symbolic execution) graph node. |
||
27 | ExplodedNode *Pred; |
||
28 | /// The flag is true if the (state of the execution) has been modified |
||
29 | /// by the checker using this context. For example, a new transition has been |
||
30 | /// added or a bug report issued. |
||
31 | bool Changed; |
||
32 | /// The tagged location, which is used to generate all new nodes. |
||
33 | const ProgramPoint Location; |
||
34 | NodeBuilder &NB; |
||
35 | |||
36 | public: |
||
37 | /// If we are post visiting a call, this flag will be set if the |
||
38 | /// call was inlined. In all other cases it will be false. |
||
39 | const bool wasInlined; |
||
40 | |||
41 | CheckerContext(NodeBuilder &builder, |
||
42 | ExprEngine &eng, |
||
43 | ExplodedNode *pred, |
||
44 | const ProgramPoint &loc, |
||
45 | bool wasInlined = false) |
||
46 | : Eng(eng), |
||
47 | Pred(pred), |
||
48 | Changed(false), |
||
49 | Location(loc), |
||
50 | NB(builder), |
||
51 | wasInlined(wasInlined) { |
||
52 | assert(Pred->getState() && |
||
53 | "We should not call the checkers on an empty state."); |
||
54 | } |
||
55 | |||
56 | AnalysisManager &getAnalysisManager() { |
||
57 | return Eng.getAnalysisManager(); |
||
58 | } |
||
59 | |||
60 | ConstraintManager &getConstraintManager() { |
||
61 | return Eng.getConstraintManager(); |
||
62 | } |
||
63 | |||
64 | StoreManager &getStoreManager() { |
||
65 | return Eng.getStoreManager(); |
||
66 | } |
||
67 | |||
68 | /// Returns the previous node in the exploded graph, which includes |
||
69 | /// the state of the program before the checker ran. Note, checkers should |
||
70 | /// not retain the node in their state since the nodes might get invalidated. |
||
71 | ExplodedNode *getPredecessor() { return Pred; } |
||
72 | const ProgramStateRef &getState() const { return Pred->getState(); } |
||
73 | |||
74 | /// Check if the checker changed the state of the execution; ex: added |
||
75 | /// a new transition or a bug report. |
||
76 | bool isDifferent() { return Changed; } |
||
77 | |||
78 | /// Returns the number of times the current block has been visited |
||
79 | /// along the analyzed path. |
||
80 | unsigned blockCount() const { |
||
81 | return NB.getContext().blockCount(); |
||
82 | } |
||
83 | |||
84 | ASTContext &getASTContext() { |
||
85 | return Eng.getContext(); |
||
86 | } |
||
87 | |||
88 | const ASTContext &getASTContext() const { return Eng.getContext(); } |
||
89 | |||
90 | const LangOptions &getLangOpts() const { |
||
91 | return Eng.getContext().getLangOpts(); |
||
92 | } |
||
93 | |||
94 | const LocationContext *getLocationContext() const { |
||
95 | return Pred->getLocationContext(); |
||
96 | } |
||
97 | |||
98 | const StackFrameContext *getStackFrame() const { |
||
99 | return Pred->getStackFrame(); |
||
100 | } |
||
101 | |||
102 | /// Return true if the current LocationContext has no caller context. |
||
103 | bool inTopFrame() const { return getLocationContext()->inTopFrame(); } |
||
104 | |||
105 | BugReporter &getBugReporter() { |
||
106 | return Eng.getBugReporter(); |
||
107 | } |
||
108 | |||
109 | const SourceManager &getSourceManager() { |
||
110 | return getBugReporter().getSourceManager(); |
||
111 | } |
||
112 | |||
113 | Preprocessor &getPreprocessor() { return getBugReporter().getPreprocessor(); } |
||
114 | |||
115 | SValBuilder &getSValBuilder() { |
||
116 | return Eng.getSValBuilder(); |
||
117 | } |
||
118 | |||
119 | SymbolManager &getSymbolManager() { |
||
120 | return getSValBuilder().getSymbolManager(); |
||
121 | } |
||
122 | |||
123 | ProgramStateManager &getStateManager() { |
||
124 | return Eng.getStateManager(); |
||
125 | } |
||
126 | |||
127 | AnalysisDeclContext *getCurrentAnalysisDeclContext() const { |
||
128 | return Pred->getLocationContext()->getAnalysisDeclContext(); |
||
129 | } |
||
130 | |||
131 | /// Get the blockID. |
||
132 | unsigned getBlockID() const { |
||
133 | return NB.getContext().getBlock()->getBlockID(); |
||
134 | } |
||
135 | |||
136 | /// If the given node corresponds to a PostStore program point, |
||
137 | /// retrieve the location region as it was uttered in the code. |
||
138 | /// |
||
139 | /// This utility can be useful for generating extensive diagnostics, for |
||
140 | /// example, for finding variables that the given symbol was assigned to. |
||
141 | static const MemRegion *getLocationRegionIfPostStore(const ExplodedNode *N) { |
||
142 | ProgramPoint L = N->getLocation(); |
||
143 | if (std::optional<PostStore> PSL = L.getAs<PostStore>()) |
||
144 | return reinterpret_cast<const MemRegion*>(PSL->getLocationValue()); |
||
145 | return nullptr; |
||
146 | } |
||
147 | |||
148 | /// Get the value of arbitrary expressions at this point in the path. |
||
149 | SVal getSVal(const Stmt *S) const { |
||
150 | return Pred->getSVal(S); |
||
151 | } |
||
152 | |||
153 | /// Returns true if the value of \p E is greater than or equal to \p |
||
154 | /// Val under unsigned comparison |
||
155 | bool isGreaterOrEqual(const Expr *E, unsigned long long Val); |
||
156 | |||
157 | /// Returns true if the value of \p E is negative. |
||
158 | bool isNegative(const Expr *E); |
||
159 | |||
160 | /// Generates a new transition in the program state graph |
||
161 | /// (ExplodedGraph). Uses the default CheckerContext predecessor node. |
||
162 | /// |
||
163 | /// @param State The state of the generated node. If not specified, the state |
||
164 | /// will not be changed, but the new node will have the checker's tag. |
||
165 | /// @param Tag The tag is used to uniquely identify the creation site. If no |
||
166 | /// tag is specified, a default tag, unique to the given checker, |
||
167 | /// will be used. Tags are used to prevent states generated at |
||
168 | /// different sites from caching out. |
||
169 | ExplodedNode *addTransition(ProgramStateRef State = nullptr, |
||
170 | const ProgramPointTag *Tag = nullptr) { |
||
171 | return addTransitionImpl(State ? State : getState(), false, nullptr, Tag); |
||
172 | } |
||
173 | |||
174 | /// Generates a new transition with the given predecessor. |
||
175 | /// Allows checkers to generate a chain of nodes. |
||
176 | /// |
||
177 | /// @param State The state of the generated node. |
||
178 | /// @param Pred The transition will be generated from the specified Pred node |
||
179 | /// to the newly generated node. |
||
180 | /// @param Tag The tag to uniquely identify the creation site. |
||
181 | ExplodedNode *addTransition(ProgramStateRef State, ExplodedNode *Pred, |
||
182 | const ProgramPointTag *Tag = nullptr) { |
||
183 | return addTransitionImpl(State, false, Pred, Tag); |
||
184 | } |
||
185 | |||
186 | /// Generate a sink node. Generating a sink stops exploration of the |
||
187 | /// given path. To create a sink node for the purpose of reporting an error, |
||
188 | /// checkers should use generateErrorNode() instead. |
||
189 | ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, |
||
190 | const ProgramPointTag *Tag = nullptr) { |
||
191 | return addTransitionImpl(State ? State : getState(), true, Pred, Tag); |
||
192 | } |
||
193 | |||
194 | /// Add a sink node to the current path of execution, halting analysis. |
||
195 | void addSink(ProgramStateRef State = nullptr, |
||
196 | const ProgramPointTag *Tag = nullptr) { |
||
197 | if (!State) |
||
198 | State = getState(); |
||
199 | addTransition(State, generateSink(State, getPredecessor())); |
||
200 | } |
||
201 | |||
202 | /// Generate a transition to a node that will be used to report |
||
203 | /// an error. This node will be a sink. That is, it will stop exploration of |
||
204 | /// the given path. |
||
205 | /// |
||
206 | /// @param State The state of the generated node. |
||
207 | /// @param Tag The tag to uniquely identify the creation site. If null, |
||
208 | /// the default tag for the checker will be used. |
||
209 | ExplodedNode *generateErrorNode(ProgramStateRef State = nullptr, |
||
210 | const ProgramPointTag *Tag = nullptr) { |
||
211 | return generateSink(State, Pred, |
||
212 | (Tag ? Tag : Location.getTag())); |
||
213 | } |
||
214 | |||
215 | /// Generate a transition to a node that will be used to report |
||
216 | /// an error. This node will be a sink. That is, it will stop exploration of |
||
217 | /// the given path. |
||
218 | /// |
||
219 | /// @param State The state of the generated node. |
||
220 | /// @param Pred The transition will be generated from the specified Pred node |
||
221 | /// to the newly generated node. |
||
222 | /// @param Tag The tag to uniquely identify the creation site. If null, |
||
223 | /// the default tag for the checker will be used. |
||
224 | ExplodedNode *generateErrorNode(ProgramStateRef State, |
||
225 | ExplodedNode *Pred, |
||
226 | const ProgramPointTag *Tag = nullptr) { |
||
227 | return generateSink(State, Pred, |
||
228 | (Tag ? Tag : Location.getTag())); |
||
229 | } |
||
230 | |||
231 | /// Generate a transition to a node that will be used to report |
||
232 | /// an error. This node will not be a sink. That is, exploration will |
||
233 | /// continue along this path. |
||
234 | /// |
||
235 | /// @param State The state of the generated node. |
||
236 | /// @param Tag The tag to uniquely identify the creation site. If null, |
||
237 | /// the default tag for the checker will be used. |
||
238 | ExplodedNode * |
||
239 | generateNonFatalErrorNode(ProgramStateRef State = nullptr, |
||
240 | const ProgramPointTag *Tag = nullptr) { |
||
241 | return addTransition(State, (Tag ? Tag : Location.getTag())); |
||
242 | } |
||
243 | |||
244 | /// Generate a transition to a node that will be used to report |
||
245 | /// an error. This node will not be a sink. That is, exploration will |
||
246 | /// continue along this path. |
||
247 | /// |
||
248 | /// @param State The state of the generated node. |
||
249 | /// @param Pred The transition will be generated from the specified Pred node |
||
250 | /// to the newly generated node. |
||
251 | /// @param Tag The tag to uniquely identify the creation site. If null, |
||
252 | /// the default tag for the checker will be used. |
||
253 | ExplodedNode * |
||
254 | generateNonFatalErrorNode(ProgramStateRef State, |
||
255 | ExplodedNode *Pred, |
||
256 | const ProgramPointTag *Tag = nullptr) { |
||
257 | return addTransition(State, Pred, (Tag ? Tag : Location.getTag())); |
||
258 | } |
||
259 | |||
260 | /// Emit the diagnostics report. |
||
261 | void emitReport(std::unique_ptr<BugReport> R) { |
||
262 | Changed = true; |
||
263 | Eng.getBugReporter().emitReport(std::move(R)); |
||
264 | } |
||
265 | |||
266 | /// Produce a program point tag that displays an additional path note |
||
267 | /// to the user. This is a lightweight alternative to the |
||
268 | /// BugReporterVisitor mechanism: instead of visiting the bug report |
||
269 | /// node-by-node to restore the sequence of events that led to discovering |
||
270 | /// a bug, you can add notes as you add your transitions. |
||
271 | /// |
||
272 | /// @param Cb Callback with 'BugReporterContext &, BugReport &' parameters. |
||
273 | /// @param IsPrunable Whether the note is prunable. It allows BugReporter |
||
274 | /// to omit the note from the report if it would make the displayed |
||
275 | /// bug path significantly shorter. |
||
276 | LLVM_ATTRIBUTE_RETURNS_NONNULL |
||
277 | const NoteTag *getNoteTag(NoteTag::Callback &&Cb, bool IsPrunable = false) { |
||
278 | return Eng.getDataTags().make<NoteTag>(std::move(Cb), IsPrunable); |
||
279 | } |
||
280 | |||
281 | /// A shorthand version of getNoteTag that doesn't require you to accept |
||
282 | /// the 'BugReporterContext' argument when you don't need it. |
||
283 | /// |
||
284 | /// @param Cb Callback only with 'BugReport &' parameter. |
||
285 | /// @param IsPrunable Whether the note is prunable. It allows BugReporter |
||
286 | /// to omit the note from the report if it would make the displayed |
||
287 | /// bug path significantly shorter. |
||
288 | const NoteTag |
||
289 | *getNoteTag(std::function<std::string(PathSensitiveBugReport &)> &&Cb, |
||
290 | bool IsPrunable = false) { |
||
291 | return getNoteTag( |
||
292 | [Cb](BugReporterContext &, |
||
293 | PathSensitiveBugReport &BR) { return Cb(BR); }, |
||
294 | IsPrunable); |
||
295 | } |
||
296 | |||
297 | /// A shorthand version of getNoteTag that doesn't require you to accept |
||
298 | /// the arguments when you don't need it. |
||
299 | /// |
||
300 | /// @param Cb Callback without parameters. |
||
301 | /// @param IsPrunable Whether the note is prunable. It allows BugReporter |
||
302 | /// to omit the note from the report if it would make the displayed |
||
303 | /// bug path significantly shorter. |
||
304 | const NoteTag *getNoteTag(std::function<std::string()> &&Cb, |
||
305 | bool IsPrunable = false) { |
||
306 | return getNoteTag([Cb](BugReporterContext &, |
||
307 | PathSensitiveBugReport &) { return Cb(); }, |
||
308 | IsPrunable); |
||
309 | } |
||
310 | |||
311 | /// A shorthand version of getNoteTag that accepts a plain note. |
||
312 | /// |
||
313 | /// @param Note The note. |
||
314 | /// @param IsPrunable Whether the note is prunable. It allows BugReporter |
||
315 | /// to omit the note from the report if it would make the displayed |
||
316 | /// bug path significantly shorter. |
||
317 | const NoteTag *getNoteTag(StringRef Note, bool IsPrunable = false) { |
||
318 | return getNoteTag( |
||
319 | [Note](BugReporterContext &, |
||
320 | PathSensitiveBugReport &) { return std::string(Note); }, |
||
321 | IsPrunable); |
||
322 | } |
||
323 | |||
324 | /// A shorthand version of getNoteTag that accepts a lambda with stream for |
||
325 | /// note. |
||
326 | /// |
||
327 | /// @param Cb Callback with 'BugReport &' and 'llvm::raw_ostream &'. |
||
328 | /// @param IsPrunable Whether the note is prunable. It allows BugReporter |
||
329 | /// to omit the note from the report if it would make the displayed |
||
330 | /// bug path significantly shorter. |
||
331 | const NoteTag *getNoteTag( |
||
332 | std::function<void(PathSensitiveBugReport &BR, llvm::raw_ostream &OS)> &&Cb, |
||
333 | bool IsPrunable = false) { |
||
334 | return getNoteTag( |
||
335 | [Cb](PathSensitiveBugReport &BR) -> std::string { |
||
336 | llvm::SmallString<128> Str; |
||
337 | llvm::raw_svector_ostream OS(Str); |
||
338 | Cb(BR, OS); |
||
339 | return std::string(OS.str()); |
||
340 | }, |
||
341 | IsPrunable); |
||
342 | } |
||
343 | |||
344 | /// Returns the word that should be used to refer to the declaration |
||
345 | /// in the report. |
||
346 | StringRef getDeclDescription(const Decl *D); |
||
347 | |||
348 | /// Get the declaration of the called function (path-sensitive). |
||
349 | const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; |
||
350 | |||
351 | /// Get the name of the called function (path-sensitive). |
||
352 | StringRef getCalleeName(const FunctionDecl *FunDecl) const; |
||
353 | |||
354 | /// Get the identifier of the called function (path-sensitive). |
||
355 | const IdentifierInfo *getCalleeIdentifier(const CallExpr *CE) const { |
||
356 | const FunctionDecl *FunDecl = getCalleeDecl(CE); |
||
357 | if (FunDecl) |
||
358 | return FunDecl->getIdentifier(); |
||
359 | else |
||
360 | return nullptr; |
||
361 | } |
||
362 | |||
363 | /// Get the name of the called function (path-sensitive). |
||
364 | StringRef getCalleeName(const CallExpr *CE) const { |
||
365 | const FunctionDecl *FunDecl = getCalleeDecl(CE); |
||
366 | return getCalleeName(FunDecl); |
||
367 | } |
||
368 | |||
369 | /// Returns true if the callee is an externally-visible function in the |
||
370 | /// top-level namespace, such as \c malloc. |
||
371 | /// |
||
372 | /// If a name is provided, the function must additionally match the given |
||
373 | /// name. |
||
374 | /// |
||
375 | /// Note that this deliberately excludes C++ library functions in the \c std |
||
376 | /// namespace, but will include C library functions accessed through the |
||
377 | /// \c std namespace. This also does not check if the function is declared |
||
378 | /// as 'extern "C"', or if it uses C++ name mangling. |
||
379 | static bool isCLibraryFunction(const FunctionDecl *FD, |
||
380 | StringRef Name = StringRef()); |
||
381 | |||
382 | /// Depending on wither the location corresponds to a macro, return |
||
383 | /// either the macro name or the token spelling. |
||
384 | /// |
||
385 | /// This could be useful when checkers' logic depends on whether a function |
||
386 | /// is called with a given macro argument. For example: |
||
387 | /// s = socket(AF_INET,..) |
||
388 | /// If AF_INET is a macro, the result should be treated as a source of taint. |
||
389 | /// |
||
390 | /// \sa clang::Lexer::getSpelling(), clang::Lexer::getImmediateMacroName(). |
||
391 | StringRef getMacroNameOrSpelling(SourceLocation &Loc); |
||
392 | |||
393 | private: |
||
394 | ExplodedNode *addTransitionImpl(ProgramStateRef State, |
||
395 | bool MarkAsSink, |
||
396 | ExplodedNode *P = nullptr, |
||
397 | const ProgramPointTag *Tag = nullptr) { |
||
398 | // The analyzer may stop exploring if it sees a state it has previously |
||
399 | // visited ("cache out"). The early return here is a defensive check to |
||
400 | // prevent accidental caching out by checker API clients. Unless there is a |
||
401 | // tag or the client checker has requested that the generated node be |
||
402 | // marked as a sink, we assume that a client requesting a transition to a |
||
403 | // state that is the same as the predecessor state has made a mistake. We |
||
404 | // return the predecessor rather than cache out. |
||
405 | // |
||
406 | // TODO: We could potentially change the return to an assertion to alert |
||
407 | // clients to their mistake, but several checkers (including |
||
408 | // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation) |
||
409 | // rely upon the defensive behavior and would need to be updated. |
||
410 | if (!State || (State == Pred->getState() && !Tag && !MarkAsSink)) |
||
411 | return Pred; |
||
412 | |||
413 | Changed = true; |
||
414 | const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location); |
||
415 | if (!P) |
||
416 | P = Pred; |
||
417 | |||
418 | ExplodedNode *node; |
||
419 | if (MarkAsSink) |
||
420 | node = NB.generateSink(LocalLoc, State, P); |
||
421 | else |
||
422 | node = NB.generateNode(LocalLoc, State, P); |
||
423 | return node; |
||
424 | } |
||
425 | }; |
||
426 | |||
427 | } // end GR namespace |
||
428 | |||
429 | } // end clang namespace |
||
430 | |||
431 | #endif |