1/* Modeling API uses and misuses via state machines.
2 Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#ifndef GCC_ANALYZER_SM_H
22#define GCC_ANALYZER_SM_H
23
24#include "analyzer/analyzer-logging.h"
25
26/* Utility functions for use by state machines. */
27
28namespace ana {
29
30class state_machine;
31class sm_context;
32class pending_diagnostic;
33class analyzer_state_graph;
34
35extern bool any_pointer_p (tree expr);
36extern bool any_pointer_p (const svalue *sval);
37
38/* An abstract base class for a state machine describing an API.
39 Manages a set of state objects, and has various virtual functions
40 for pattern-matching on statements. */
41
42class state_machine : public log_user
43{
44public:
45 /* States are represented by immutable objects, owned by the state
46 machine. */
47 class state
48 {
49 public:
50 state (const char *name, unsigned id) : m_name (name), m_id (id) {}
51 virtual ~state () {}
52
53 const char *get_name () const { return m_name; }
54 virtual void dump_to_pp (pretty_printer *pp) const;
55 virtual std::unique_ptr<json::value> to_json () const;
56
57 unsigned get_id () const { return m_id; }
58
59 private:
60 const char *m_name;
61 unsigned m_id;
62 };
63 typedef const state_machine::state *state_t;
64
65 state_machine (const char *name, logger *logger);
66 virtual ~state_machine () {}
67
68 /* Should states be inherited from a parent region to a child region,
69 when first accessing a child region?
70 For example we should inherit the taintedness of a subregion,
71 but we should not inherit the "malloc:non-null" state of a field
72 within a heap-allocated struct. */
73 virtual bool inherited_state_p () const = 0;
74
75 /* A vfunc for more general handling of inheritance. */
76 virtual state_t
77 alt_get_inherited_state (const sm_state_map &,
78 const svalue *,
79 const extrinsic_state &) const
80 {
81 return nullptr;
82 }
83
84 virtual bool
85 has_alt_get_inherited_state_p () const
86 {
87 return false;
88 }
89
90 virtual state_machine::state_t get_default_state (const svalue *) const
91 {
92 return m_start;
93 }
94
95 const char *get_name () const { return m_name; }
96
97 state_t get_state_by_name (const char *name) const;
98
99 /* Return true if STMT is a function call recognized by this sm. */
100 virtual bool on_stmt (sm_context &sm_ctxt,
101 const gimple *stmt) const = 0;
102
103 virtual void on_phi (sm_context &sm_ctxt ATTRIBUTE_UNUSED,
104 const gphi *phi ATTRIBUTE_UNUSED,
105 tree rhs ATTRIBUTE_UNUSED) const
106 {
107 }
108
109 virtual void
110 check_call_preconditions (sm_context &sm_ctxt ATTRIBUTE_UNUSED,
111 const call_details &cd ATTRIBUTE_UNUSED) const
112 {
113 }
114
115 virtual void on_condition (sm_context &sm_ctxt ATTRIBUTE_UNUSED,
116 const svalue *lhs ATTRIBUTE_UNUSED,
117 enum tree_code op ATTRIBUTE_UNUSED,
118 const svalue *rhs ATTRIBUTE_UNUSED) const
119 {
120 }
121
122 virtual void
123 on_bounded_ranges (sm_context &sm_ctxt ATTRIBUTE_UNUSED,
124 const svalue &sval ATTRIBUTE_UNUSED,
125 const bounded_ranges &ranges ATTRIBUTE_UNUSED) const
126 {
127 }
128
129 virtual void
130 on_pop_frame (sm_state_map *smap ATTRIBUTE_UNUSED,
131 const frame_region *frame_reg ATTRIBUTE_UNUSED) const
132 {
133 }
134
135 /* Return true if it safe to discard the given state (to help
136 when simplifying state objects).
137 States that need leak detection should return false. */
138 virtual bool can_purge_p (state_t s) const = 0;
139
140 /* Called when VAR leaks (and !can_purge_p). */
141 virtual std::unique_ptr<pending_diagnostic>
142 on_leak (tree var ATTRIBUTE_UNUSED,
143 const program_state *old_state,
144 const program_state *new_state) const;
145
146 /* Return true if S should be reset to "start" for values passed (or reachable
147 from) calls to unknown functions. IS_MUTABLE is true for pointers as
148 non-const, false if only passed as const-pointers.
149
150 For example, in sm-malloc.cc, an on-stack ptr doesn't stop being
151 stack-allocated when passed to an unknown fn, but a malloc-ed pointer
152 could be freed when passed to an unknown fn (unless passed as "const"). */
153 virtual bool reset_when_passed_to_unknown_fn_p (state_t s ATTRIBUTE_UNUSED,
154 bool is_mutable) const
155 {
156 return is_mutable;
157 }
158
159 /* Attempt to get a state for the merger of STATE_A and STATE_B,
160 or return nullptr if merging shouldn't occur, so that differences
161 between sm-state will lead to separate exploded nodes.
162
163 Most state machines will only merge equal states, but can
164 override maybe_get_merged_states_nonequal to support mergers
165 of certain non-equal states. */
166 state_t maybe_get_merged_state (state_t state_a,
167 state_t state_b) const
168 {
169 if (state_a == state_b)
170 return state_a;
171 return maybe_get_merged_states_nonequal (state_a, state_b);
172 }
173
174 /* Base implementation of hook for maybe_get_merged_state on non-equal
175 states. */
176 virtual state_t
177 maybe_get_merged_states_nonequal (state_t state_a ATTRIBUTE_UNUSED,
178 state_t state_b ATTRIBUTE_UNUSED) const
179 {
180 /* By default, non-equal sm states should inhibit merger of enodes. */
181 return nullptr;
182 }
183
184 void validate (state_t s) const;
185
186 void dump_to_pp (pretty_printer *pp) const;
187
188 std::unique_ptr<json::object> to_json () const;
189
190 state_t get_start_state () const { return m_start; }
191
192 virtual void
193 add_state_to_state_graph (analyzer_state_graph &out_state_graph,
194 const svalue &sval,
195 state_machine::state_t state) const;
196
197 virtual void
198 add_global_state_to_state_graph (analyzer_state_graph &out_state_graph,
199 state_machine::state_t state) const;
200
201protected:
202 state_t add_state (const char *name);
203 state_t add_custom_state (state *s)
204 {
205 m_states.safe_push (obj: s);
206 return s;
207 }
208
209 unsigned alloc_state_id () { return m_next_state_id++; }
210
211private:
212 DISABLE_COPY_AND_ASSIGN (state_machine);
213
214 const char *m_name;
215
216 /* States are owned by the state_machine. */
217 auto_delete_vec<state> m_states;
218
219 unsigned m_next_state_id;
220
221protected:
222 /* Must be inited after m_next_state_id. */
223 state_t m_start;
224};
225
226/* Abstract base class for state machines to pass to
227 sm_context::on_custom_transition for handling non-standard transitions
228 (e.g. adding a node and edge to simulate registering a callback and having
229 the callback be called later). */
230
231class custom_transition
232{
233public:
234 virtual ~custom_transition () {}
235 virtual void impl_transition (exploded_graph *eg,
236 exploded_node *src_enode,
237 int sm_idx) = 0;
238};
239
240/* Abstract base class giving an interface for the state machine to call
241 the checker engine, at a particular code location. */
242
243class sm_context
244{
245public:
246 virtual ~sm_context () {}
247
248 /* Get the fndecl used at call, or NULL_TREE.
249 Use in preference to gimple_call_fndecl (and gimple_call_addr_fndecl),
250 since it can look through function pointer assignments and
251 other callback handling. */
252 virtual tree get_fndecl_for_call (const gcall &call) = 0;
253
254 /* Get the old state of VAR. */
255 virtual state_machine::state_t get_state (tree var) = 0;
256 virtual state_machine::state_t get_state (const svalue *) = 0;
257
258 /* Set the next state of VAR to be TO, recording the "origin" of the
259 state as ORIGIN. */
260 virtual void set_next_state (tree var,
261 state_machine::state_t to,
262 tree origin = NULL_TREE) = 0;
263 virtual void set_next_state (const svalue *var,
264 state_machine::state_t to,
265 tree origin = NULL_TREE) = 0;
266
267 /* Called by state_machine in response to pattern matches:
268 if VAR is in state FROM, transition it to state TO, potentially
269 recording the "origin" of the state as ORIGIN. */
270 void on_transition (tree var,
271 state_machine::state_t from,
272 state_machine::state_t to,
273 tree origin = NULL_TREE)
274 {
275 state_machine::state_t current = get_state (var);
276 if (current == from)
277 set_next_state (var, to, origin);
278 }
279
280 void on_transition (const svalue *var,
281 state_machine::state_t from,
282 state_machine::state_t to,
283 tree origin = NULL_TREE)
284 {
285 state_machine::state_t current = get_state (var);
286 if (current == from)
287 set_next_state (var, to, origin);
288 }
289
290 /* Called by state_machine in response to pattern matches:
291 issue a diagnostic D about VAR. */
292 virtual void warn (tree var,
293 std::unique_ptr<pending_diagnostic> d) = 0;
294 virtual void warn (const svalue *var,
295 std::unique_ptr<pending_diagnostic> d) = 0;
296
297 /* For use when generating trees when creating pending_diagnostics, so that
298 rather than e.g.
299 "double-free of '<unknown>'"
300 we can print:
301 "double-free of 'inbuf.data'". */
302 virtual tree get_diagnostic_tree (tree expr)
303 {
304 return expr;
305 }
306 virtual tree get_diagnostic_tree (const svalue *) = 0;
307
308 virtual state_machine::state_t get_global_state () const = 0;
309 virtual void set_global_state (state_machine::state_t) = 0;
310
311 virtual void clear_all_per_svalue_state () = 0;
312
313 /* A vfunc for handling custom transitions, such as when registering
314 a signal handler. */
315 virtual void on_custom_transition (custom_transition *transition) = 0;
316
317 /* If STMT is an assignment known to assign zero to its LHS, return
318 the LHS.
319 Otherwise return NULL_TREE. */
320 virtual tree is_zero_assignment (const gimple *stmt) = 0;
321
322 virtual path_context *get_path_context () const
323 {
324 return nullptr;
325 }
326
327 /* Are we handling an external function with unknown side effects? */
328 virtual bool unknown_side_effects_p () const { return false; }
329
330 virtual const program_state *get_old_program_state () const = 0;
331 virtual const program_state *get_new_program_state () const = 0;
332
333 const region_model *get_old_region_model () const;
334
335 /* Get the location a diagnostic would be emitted at. */
336 virtual location_t get_emission_location () const = 0;
337
338protected:
339 sm_context (int sm_idx, const state_machine &sm)
340 : m_sm_idx (sm_idx), m_sm (sm) {}
341
342 int m_sm_idx;
343 const state_machine &m_sm;
344};
345
346
347/* The various state_machine subclasses are hidden in their respective
348 implementation files. */
349
350extern std::vector<std::unique_ptr<state_machine>>
351make_checkers (logger *logger);
352
353extern std::unique_ptr<state_machine> make_malloc_state_machine (logger *);
354extern std::unique_ptr<state_machine> make_fileptr_state_machine (logger *);
355extern std::unique_ptr<state_machine> make_taint_state_machine (logger *);
356extern std::unique_ptr<state_machine> make_sensitive_state_machine (logger *);
357extern std::unique_ptr<state_machine> make_signal_state_machine (logger *);
358extern std::unique_ptr<state_machine> make_pattern_test_state_machine (logger *);
359extern std::unique_ptr<state_machine> make_va_list_state_machine (logger *);
360extern std::unique_ptr<state_machine> make_fd_state_machine (logger *);
361
362} // namespace ana
363
364#endif /* GCC_ANALYZER_SM_H */
365

source code of gcc/analyzer/sm.h