xref: /freebsd/contrib/llvm-project/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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)
Eng(eng)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 
getAnalysisManager()56   AnalysisManager &getAnalysisManager() {
57     return Eng.getAnalysisManager();
58   }
59 
getConstraintManager()60   ConstraintManager &getConstraintManager() {
61     return Eng.getConstraintManager();
62   }
63 
getStoreManager()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.
getPredecessor()71   ExplodedNode *getPredecessor() { return Pred; }
getState()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.
isDifferent()76   bool isDifferent() { return Changed; }
77 
78   /// Returns the number of times the current block has been visited
79   /// along the analyzed path.
blockCount()80   unsigned blockCount() const {
81     return NB.getContext().blockCount();
82   }
83 
getASTContext()84   ASTContext &getASTContext() {
85     return Eng.getContext();
86   }
87 
getASTContext()88   const ASTContext &getASTContext() const { return Eng.getContext(); }
89 
getLangOpts()90   const LangOptions &getLangOpts() const {
91     return Eng.getContext().getLangOpts();
92   }
93 
getLocationContext()94   const LocationContext *getLocationContext() const {
95     return Pred->getLocationContext();
96   }
97 
getStackFrame()98   const StackFrameContext *getStackFrame() const {
99     return Pred->getStackFrame();
100   }
101 
102   /// Return true if the current LocationContext has no caller context.
inTopFrame()103   bool inTopFrame() const { return getLocationContext()->inTopFrame();  }
104 
getBugReporter()105   BugReporter &getBugReporter() {
106     return Eng.getBugReporter();
107   }
108 
getSourceManager()109   const SourceManager &getSourceManager() {
110     return getBugReporter().getSourceManager();
111   }
112 
getPreprocessor()113   Preprocessor &getPreprocessor() { return getBugReporter().getPreprocessor(); }
114 
getSValBuilder()115   SValBuilder &getSValBuilder() {
116     return Eng.getSValBuilder();
117   }
118 
getSymbolManager()119   SymbolManager &getSymbolManager() {
120     return getSValBuilder().getSymbolManager();
121   }
122 
getStateManager()123   ProgramStateManager &getStateManager() {
124     return Eng.getStateManager();
125   }
126 
getCurrentAnalysisDeclContext()127   AnalysisDeclContext *getCurrentAnalysisDeclContext() const {
128     return Pred->getLocationContext()->getAnalysisDeclContext();
129   }
130 
131   /// Get the blockID.
getBlockID()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.
getLocationRegionIfPostStore(const ExplodedNode * N)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.
getSVal(const Stmt * S)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.
emitReport(std::unique_ptr<BugReport> R)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 = std::string(Note)](BugReporterContext &,
320                PathSensitiveBugReport &) { return 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).
getCalleeIdentifier(const CallExpr * CE)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).
getCalleeName(const CallExpr * CE)364   StringRef getCalleeName(const CallExpr *CE) const {
365     const FunctionDecl *FunDecl = getCalleeDecl(CE);
366     return getCalleeName(FunDecl);
367   }
368 
369   /// Returns true if the given function is an externally-visible function in
370   /// the 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 also accepts functions from the \c std namespace (because
376   /// headers like <cstdlib> declare them there) and does not check if the
377   /// function is declared as 'extern "C"' or if it uses C++ name mangling.
378   static bool isCLibraryFunction(const FunctionDecl *FD,
379                                  StringRef Name = StringRef());
380 
381   /// In builds that use source hardening (-D_FORTIFY_SOURCE), many standard
382   /// functions are implemented as macros that expand to calls of hardened
383   /// functions that take additional arguments compared to the "usual"
384   /// variant and perform additional input validation. For example, a `memcpy`
385   /// call may expand to `__memcpy_chk()` or `__builtin___memcpy_chk()`.
386   ///
387   /// This method returns true if `FD` declares a fortified variant of the
388   /// standard library function `Name`.
389   ///
390   /// NOTE: This method relies on heuristics; extend it if you need to handle a
391   /// hardened variant that's not yet covered by it.
392   static bool isHardenedVariantOf(const FunctionDecl *FD, StringRef Name);
393 
394   /// Depending on wither the location corresponds to a macro, return
395   /// either the macro name or the token spelling.
396   ///
397   /// This could be useful when checkers' logic depends on whether a function
398   /// is called with a given macro argument. For example:
399   ///   s = socket(AF_INET,..)
400   /// If AF_INET is a macro, the result should be treated as a source of taint.
401   ///
402   /// \sa clang::Lexer::getSpelling(), clang::Lexer::getImmediateMacroName().
403   StringRef getMacroNameOrSpelling(SourceLocation &Loc);
404 
405 private:
406   ExplodedNode *addTransitionImpl(ProgramStateRef State,
407                                  bool MarkAsSink,
408                                  ExplodedNode *P = nullptr,
409                                  const ProgramPointTag *Tag = nullptr) {
410     // The analyzer may stop exploring if it sees a state it has previously
411     // visited ("cache out"). The early return here is a defensive check to
412     // prevent accidental caching out by checker API clients. Unless there is a
413     // tag or the client checker has requested that the generated node be
414     // marked as a sink, we assume that a client requesting a transition to a
415     // state that is the same as the predecessor state has made a mistake. We
416     // return the predecessor rather than cache out.
417     //
418     // TODO: We could potentially change the return to an assertion to alert
419     // clients to their mistake, but several checkers (including
420     // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation)
421     // rely upon the defensive behavior and would need to be updated.
422     if (!State || (State == Pred->getState() && !Tag && !MarkAsSink))
423       return Pred;
424 
425     Changed = true;
426     const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : Location);
427     if (!P)
428       P = Pred;
429 
430     ExplodedNode *node;
431     if (MarkAsSink)
432       node = NB.generateSink(LocalLoc, State, P);
433     else
434       node = NB.generateNode(LocalLoc, State, P);
435     return node;
436   }
437 };
438 
439 } // end GR namespace
440 
441 } // end clang namespace
442 
443 #endif
444