Line data Source code
1 : /* Classes for analyzer diagnostics.
2 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H
22 : #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
23 :
24 : #include "diagnostics/metadata.h"
25 : #include "analyzer/sm.h"
26 : #include "analyzer/state-transition.h"
27 :
28 : namespace ana {
29 :
30 : /* A bundle of information about things that are of interest to a
31 : pending_diagnostic:
32 :
33 : * a set of regions that are pertinent to the
34 : diagnostic, so that we can notify the user about when they
35 : were created.
36 :
37 : * a set of regions that a pertinent value for the diagnostic was
38 : read from, so that we can notify the user about where those values
39 : came from. */
40 :
41 16644 : struct interesting_t
42 : {
43 : void add_region_creation (const region *reg);
44 :
45 : void add_read_region (const region *reg, std::string debug_desc);
46 :
47 : void dump_to_pp (pretty_printer *pp, bool simple) const;
48 :
49 : auto_vec<const region *> m_region_creation;
50 : std::vector<diagnostic_state> m_read_regions;
51 : };
52 :
53 : /* Various bundles of information used for generating more precise
54 : messages for events within a diagnostic path, for passing to the
55 : various "describe_*" vfuncs of pending_diagnostic. See those
56 : for more information. */
57 :
58 : namespace evdesc {
59 :
60 : /* For use by pending_diagnostic::describe_state_change. */
61 :
62 : struct state_change
63 : {
64 4645 : state_change (tree expr,
65 : tree origin,
66 : state_machine::state_t old_state,
67 : state_machine::state_t new_state,
68 : diagnostics::paths::event_id_t event_id,
69 : const state_change_event &event)
70 4645 : : m_expr (expr), m_origin (origin),
71 4645 : m_old_state (old_state), m_new_state (new_state),
72 4645 : m_event_id (event_id), m_event (event)
73 : {}
74 :
75 22 : bool is_global_p () const { return m_expr == NULL_TREE; }
76 :
77 : tree m_expr;
78 : tree m_origin;
79 : state_machine::state_t m_old_state;
80 : state_machine::state_t m_new_state;
81 : diagnostics::paths::event_id_t m_event_id;
82 : const state_change_event &m_event;
83 : };
84 :
85 : /* For use by pending_diagnostic::describe_origin_of_state. */
86 :
87 : struct origin_of_state
88 : {
89 60 : origin_of_state (tree dst_reg_expr)
90 60 : : m_dst_reg_expr (dst_reg_expr)
91 : {
92 60 : gcc_assert (m_dst_reg_expr);
93 60 : }
94 :
95 : tree m_dst_reg_expr;
96 : };
97 :
98 : /* For use by pending_diagnostic::describe_call_with_state. */
99 :
100 : struct call_with_state
101 : {
102 440 : call_with_state (tree caller_fndecl, tree callee_fndecl,
103 : tree expr, state_machine::state_t state,
104 : const state_transition_at_call *state_trans)
105 440 : : m_caller_fndecl (caller_fndecl),
106 440 : m_callee_fndecl (callee_fndecl),
107 440 : m_expr (expr),
108 440 : m_state (state),
109 440 : m_state_trans (state_trans)
110 : {
111 394 : if (state_trans)
112 46 : m_src_event_id = state_trans->get_src_event_id ();
113 : }
114 :
115 : tree m_caller_fndecl;
116 : tree m_callee_fndecl;
117 : tree m_expr;
118 : state_machine::state_t m_state;
119 : const state_transition_at_call *m_state_trans;
120 : diagnostics::paths::event_id_t m_src_event_id;
121 : };
122 :
123 : /* For use by pending_diagnostic::describe_return_of_state. */
124 :
125 : struct return_of_state
126 : {
127 157 : return_of_state (tree caller_fndecl, tree callee_fndecl,
128 : state_machine::state_t state,
129 : const state_transition_at_return *state_trans)
130 157 : : m_caller_fndecl (caller_fndecl),
131 157 : m_callee_fndecl (callee_fndecl),
132 157 : m_state (state),
133 157 : m_state_trans (state_trans)
134 : {
135 133 : if (state_trans)
136 24 : m_src_event_id = state_trans->get_src_event_id ();
137 : }
138 :
139 : tree m_caller_fndecl;
140 : tree m_callee_fndecl;
141 : state_machine::state_t m_state;
142 : const state_transition_at_return *m_state_trans;
143 : diagnostics::paths::event_id_t m_src_event_id;
144 : };
145 :
146 : /* For use by pending_diagnostic::describe_copy_of_state. */
147 :
148 : struct copy_of_state
149 : {
150 12 : copy_of_state (tree src_reg_expr,
151 : diagnostics::paths::event_id_t src_event_id,
152 : tree dst_reg_expr)
153 12 : : m_src_reg_expr (src_reg_expr),
154 12 : m_src_event_id (src_event_id),
155 12 : m_dst_reg_expr (dst_reg_expr)
156 : {
157 12 : gcc_assert (m_src_reg_expr);
158 12 : gcc_assert (m_dst_reg_expr);
159 12 : }
160 :
161 : tree m_src_reg_expr;
162 : diagnostics::paths::event_id_t m_src_event_id;
163 : tree m_dst_reg_expr;
164 : };
165 :
166 : /* For use by pending_diagnostic::describe_use_of_state. */
167 :
168 : struct use_of_state
169 : {
170 16 : use_of_state (tree src_reg_expr,
171 : diagnostics::paths::event_id_t src_event_id)
172 16 : : m_src_reg_expr (src_reg_expr),
173 16 : m_src_event_id (src_event_id)
174 : {
175 16 : gcc_assert (m_src_reg_expr);
176 16 : }
177 :
178 : tree m_src_reg_expr;
179 : diagnostics::paths::event_id_t m_src_event_id;
180 : };
181 :
182 : /* For use by pending_diagnostic::describe_final_event. */
183 :
184 : struct final_event
185 : {
186 8280 : final_event (tree expr, state_machine::state_t state,
187 : const warning_event &event)
188 8280 : : m_expr (expr), m_state (state), m_event (event)
189 : {}
190 :
191 : tree m_expr;
192 : state_machine::state_t m_state;
193 : const warning_event &m_event;
194 : };
195 :
196 : } /* end of namespace evdesc */
197 :
198 : /* A bundle of information for use by implementations of the
199 : pending_diagnostic::emit vfunc.
200 :
201 : The rich_location will have already been populated with a
202 : diagnostics::paths::path. */
203 :
204 : class diagnostic_emission_context
205 : {
206 : public:
207 4161 : diagnostic_emission_context (const saved_diagnostic &sd,
208 : rich_location &rich_loc,
209 : diagnostics::metadata &metadata,
210 : logger *logger)
211 4161 : : m_sd (sd),
212 4161 : m_rich_loc (rich_loc),
213 4161 : m_metadata (metadata),
214 4161 : m_logger (logger)
215 : {
216 : }
217 :
218 : const pending_diagnostic &get_pending_diagnostic () const;
219 :
220 : bool warn (const char *, ...) ATTRIBUTE_GCC_DIAG (2,3);
221 : void inform (const char *, ...) ATTRIBUTE_GCC_DIAG (2,3);
222 :
223 975 : location_t get_location () const { return m_rich_loc.get_loc (); }
224 674 : logger *get_logger () const { return m_logger; }
225 :
226 3362 : void add_cwe (int cwe) { m_metadata.add_cwe (cwe); }
227 27 : void add_rule (const diagnostics::metadata::rule &r)
228 : {
229 27 : m_metadata.add_rule (r);
230 : }
231 :
232 : private:
233 : const saved_diagnostic &m_sd;
234 : rich_location &m_rich_loc;
235 : diagnostics::metadata &m_metadata;
236 : logger *m_logger;
237 : };
238 :
239 : /* An abstract base class for capturing information about a diagnostic in
240 : a form that is ready to emit at a later point (or be rejected).
241 : Each kind of diagnostic will have a concrete subclass of
242 : pending_diagnostic.
243 :
244 : Normally, gcc diagnostics are emitted using va_list, which can't be
245 : portably stored for later use, so we have to use an "emit" virtual
246 : function.
247 :
248 : This class also supports comparison, so that multiple pending_diagnostic
249 : instances can be de-duplicated.
250 :
251 : As well as emitting a diagnostic, the class has various "precision of
252 : wording" virtual functions, for generating descriptions for events
253 : within a diagnostic path. These are optional, but implementing these
254 : allows for more precise wordings than the more generic
255 : implementation. */
256 :
257 6067 : class pending_diagnostic
258 : {
259 : public:
260 0 : virtual ~pending_diagnostic () {}
261 :
262 : /* Vfunc to get the command-line option used when emitting the diagnostic,
263 : or zero if there is none.
264 : Used by diagnostic_manager for early rejection of diagnostics (to avoid
265 : having to generate feasible execution paths for them). */
266 : virtual int get_controlling_option () const = 0;
267 :
268 : /* Vfunc to give the diagnostic the chance to terminate the execution
269 : path being explored. By default, don't terminate the path. */
270 7159 : virtual bool terminate_path_p () const { return false; }
271 :
272 : /* Vfunc for emitting the diagnostic.
273 : Return true if a diagnostic is actually emitted. */
274 : virtual bool emit (diagnostic_emission_context &) = 0;
275 :
276 : /* Hand-coded RTTI: get an ID for the subclass. */
277 : virtual const char *get_kind () const = 0;
278 :
279 : /* A vfunc for identifying "use of uninitialized value". */
280 218 : virtual bool use_of_uninit_p () const { return false; }
281 :
282 : /* Compare for equality with OTHER, which might be of a different
283 : subclass. */
284 :
285 6230 : bool equal_p (const pending_diagnostic &other) const
286 : {
287 : /* Check for pointer equality on the IDs from get_kind. */
288 6230 : if (get_kind () != other.get_kind ())
289 : return false;
290 : /* Call vfunc now we know they have the same ID: */
291 6182 : return subclass_equal_p (other);
292 : }
293 :
294 : /* A vfunc for testing for equality, where we've already
295 : checked they have the same ID. See pending_diagnostic_subclass
296 : below for a convenience subclass for implementing this. */
297 : virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
298 :
299 : /* Return true if T1 and T2 are "the same" for the purposes of
300 : diagnostic deduplication. */
301 : static bool same_tree_p (tree t1, tree t2);
302 :
303 : /* Vfunc for fixing up locations, e.g. to avoid unwinding
304 : inside specific macros. PRIMARY is true for the primary location
305 : for the diagnostic, and FALSE for events in their paths. */
306 : virtual location_t fixup_location (location_t loc, bool primary) const;
307 :
308 : /* Precision-of-wording vfunc for describing a critical state change
309 : within the diagnostic path.
310 :
311 : For example, a double-free diagnostic might use the descriptions:
312 : - "first 'free' happens here"
313 : - "second 'free' happens here"
314 : for the pertinent events, whereas a use-after-free might use the
315 : descriptions:
316 : - "freed here"
317 : - "use after free here"
318 : Note how in both cases the first event is a "free": the best
319 : description to use depends on the diagnostic.
320 :
321 : Print the description to PP and return true,
322 : or do nothing and return false. */
323 :
324 0 : virtual bool describe_state_change (pretty_printer &,
325 : const evdesc::state_change &)
326 : {
327 : /* Default no-op implementation. */
328 0 : return false;
329 : }
330 :
331 : /* Vfunc for implementing event::get_meaning for
332 : state_change_event. */
333 : virtual diagnostics::paths::event::meaning
334 2 : get_meaning_for_state_change (const evdesc::state_change &) const
335 : {
336 : /* Default no-op implementation. */
337 2 : return diagnostics::paths::event::meaning ();
338 : }
339 :
340 : /* Precision-of-wording vfunc for use in describing state_transition_event
341 : instances of state_transition::kind::origin.
342 : Return true if a description of the event was printed to the
343 : pretty-printer, or false otherwise.
344 : For example, a divide-by-zero diagnostic might use:
345 : "zero value originates here"
346 : at the point where the zero comes from. */
347 16 : virtual bool describe_origin_of_state (pretty_printer &,
348 : const evdesc::origin_of_state &)
349 : {
350 : /* Default no-op implementation. */
351 16 : return false;
352 : }
353 :
354 : /* Precision-of-wording vfunc for describing an interprocedural call
355 : carrying critial state for the diagnostic, from caller to callee.
356 :
357 : For example a double-free diagnostic might use:
358 : - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'"
359 : to make it clearer how the freed value moves from caller to
360 : callee. */
361 :
362 80 : virtual bool describe_call_with_state (pretty_printer &,
363 : const evdesc::call_with_state &)
364 : {
365 : /* Default no-op implementation. */
366 80 : return false;
367 : }
368 :
369 : /* Precision-of-wording vfunc for describing an interprocedural return
370 : within the diagnostic path that carries critial state for the
371 : diagnostic, from callee back to caller.
372 :
373 : For example, a deref-of-unchecked-malloc diagnostic might use:
374 : - "returning possibly-NULL pointer to 'make_obj' from 'allocator'"
375 : to make it clearer how the unchecked value moves from callee
376 : back to caller. */
377 :
378 83 : virtual bool describe_return_of_state (pretty_printer &,
379 : const evdesc::return_of_state &)
380 : {
381 : /* Default no-op implementation. */
382 83 : return false;
383 : }
384 :
385 : /* Precision-of-wording vfunc for use in describing state_transition_event
386 : instances of state_transition::kind::copy.
387 : Return true if a description of the event was printed to the
388 : pretty-printer, or false otherwise.
389 : For example, a divide-by-zero diagnostic might use:
390 : "copying zero value from (3) from 'x' to 'y'". */
391 0 : virtual bool describe_copy_of_state (pretty_printer &,
392 : const evdesc::copy_of_state &)
393 : {
394 : /* Default no-op implementation. */
395 0 : return false;
396 : }
397 :
398 : /* Precision-of-wording vfunc for use in describing state_transition_event
399 : instances of state_transition::kind::use.
400 : Return true if a description of the event was printed to the
401 : pretty-printer, or false otherwise.
402 : For example, a divide-by-zero diagnostic might use:
403 : "using zero value from (7) from 'y'". */
404 0 : virtual bool describe_use_of_state (pretty_printer &,
405 : const evdesc::use_of_state &)
406 : {
407 : /* Default no-op implementation. */
408 0 : return false;
409 : }
410 :
411 : /* Precision-of-wording vfunc for describing the final event within a
412 : diagnostic path.
413 :
414 : For example a double-free diagnostic might use:
415 : - "second 'free' here; first 'free' was at (3)"
416 : and a use-after-free might use
417 : - "use after 'free' here; memory was freed at (2)". */
418 :
419 619 : virtual bool describe_final_event (pretty_printer &,
420 : const evdesc::final_event &)
421 : {
422 : /* Default no-op implementation. */
423 619 : return false;
424 : }
425 :
426 : /* End of precision-of-wording vfuncs. */
427 :
428 : /* Vfunc for adding a function_entry_event to a checker_path, so that e.g.
429 : the infinite recursion diagnostic can add a custom event subclass
430 : that annotates recursively entering a function. */
431 :
432 : virtual void
433 : add_function_entry_event (const exploded_edge &eedge,
434 : checker_path *emission_path,
435 : const state_transition_at_call *state_trans);
436 :
437 : /* Vfunc for extending/overriding creation of the events for an
438 : exploded_edge, allowing for custom events to be created that are
439 : pertinent to a particular pending_diagnostic subclass.
440 :
441 : For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a
442 : custom event showing when the pertinent stack frame is popped
443 : (and thus the point at which the jmp_buf becomes invalid). */
444 :
445 42211 : virtual bool maybe_add_custom_events_for_eedge (const exploded_edge &,
446 : checker_path *)
447 : {
448 42211 : return false;
449 : }
450 :
451 : /* Vfunc for adding a call_event to a checker_path, so that e.g.
452 : the varargs diagnostics can add a custom event subclass that annotates
453 : the variadic arguments. */
454 : virtual void add_call_event (const exploded_edge &,
455 : const gcall &call_stmt,
456 : checker_path &emission_path,
457 : const state_transition_at_call *state_trans);
458 :
459 : /* Vfunc for adding any events for the creation of regions identified
460 : by the mark_interesting_stuff vfunc.
461 : See the comment for class region_creation_event. */
462 : virtual void add_region_creation_events (const region *reg,
463 : tree capacity,
464 : const event_loc_info &loc_info,
465 : checker_path &emission_path);
466 :
467 : /* Vfunc for adding the final warning_event to a checker_path, so that e.g.
468 : the infinite recursion diagnostic can have its diagnostic appear at
469 : the callsite, but the final event in the path be at the entrypoint
470 : of the called function. */
471 : virtual void add_final_event (const state_machine *sm,
472 : const exploded_node *enode,
473 : const event_loc_info &loc_info,
474 : tree var, state_machine::state_t state,
475 : checker_path *emission_path);
476 :
477 : virtual const program_state *
478 3053 : get_final_state () const
479 : {
480 3053 : return nullptr;
481 : }
482 :
483 : /* Vfunc for determining that this pending_diagnostic supercedes OTHER,
484 : and that OTHER should therefore not be emitted.
485 : They have already been tested for being at the same stmt. */
486 :
487 : virtual bool
488 4795 : supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
489 : {
490 4795 : return false;
491 : }
492 :
493 : /* Vfunc for registering additional information of interest to this
494 : diagnostic. */
495 :
496 5512 : virtual void mark_interesting_stuff (interesting_t *)
497 : {
498 : /* Default no-op implementation. */
499 5512 : }
500 :
501 : /* Vfunc to give diagnostic subclasses the opportunity to reject diagnostics
502 : by imposing their own additional feasibility checks on the path to a
503 : given feasible_node. */
504 4592 : virtual bool check_valid_fpath_p (const feasible_node &) const
505 : {
506 : /* Default implementation: accept this path. */
507 4592 : return true;
508 : }
509 :
510 : /* Vfunc for use in SARIF output to give pending_diagnostic subclasses
511 : the opportunity to add diagnostic-specific properties to the SARIF
512 : "result" object for the diagnostic.
513 : This is intended for use when debugging a diagnostic. */
514 : virtual void
515 24 : maybe_add_sarif_properties (diagnostics::sarif_object &/*result_obj*/) const
516 : {
517 : /* Default no-op implementation. */
518 24 : }
519 : };
520 :
521 : /* A template to make it easier to make subclasses of pending_diagnostic.
522 :
523 : This uses the curiously-recurring template pattern, to implement
524 : pending_diagnostic::subclass_equal_p by casting and calling
525 : the operator==
526 :
527 : This assumes that BASE_OTHER has already been checked to have
528 : been of the same subclass (which pending_diagnostic::equal_p does). */
529 :
530 : template <class Subclass>
531 3575 : class pending_diagnostic_subclass : public pending_diagnostic
532 : {
533 : public:
534 2491 : bool subclass_equal_p (const pending_diagnostic &base_other) const
535 : final override
536 : {
537 2491 : const Subclass &other = (const Subclass &)base_other;
538 2491 : return *(const Subclass*)this == other;
539 : }
540 : };
541 :
542 : /* An abstract base class for capturing additional notes that are to be
543 : emitted with a diagnostic. */
544 :
545 199 : class pending_note
546 : {
547 : public:
548 0 : virtual ~pending_note () {}
549 :
550 : /* Hand-coded RTTI: get an ID for the subclass. */
551 : virtual const char *get_kind () const = 0;
552 :
553 : /* Vfunc for emitting the note. */
554 : virtual void emit () const = 0;
555 :
556 745 : bool equal_p (const pending_note &other) const
557 : {
558 : /* Check for pointer equality on the IDs from get_kind. */
559 745 : if (get_kind () != other.get_kind ())
560 : return false;
561 : /* Call vfunc now we know they have the same ID: */
562 745 : return subclass_equal_p (other);
563 : }
564 :
565 : /* A vfunc for testing for equality, where we've already
566 : checked they have the same ID. See pending_note_subclass
567 : below for a convenience subclass for implementing this. */
568 : virtual bool subclass_equal_p (const pending_note &other) const = 0;
569 : };
570 :
571 : /* Analogous to pending_diagnostic_subclass, but for pending_note. */
572 :
573 : template <class Subclass>
574 22 : class pending_note_subclass : public pending_note
575 : {
576 : public:
577 745 : bool subclass_equal_p (const pending_note &base_other) const
578 : final override
579 : {
580 745 : const Subclass &other = (const Subclass &)base_other;
581 745 : return *(const Subclass*)this == other;
582 : }
583 : };
584 :
585 : } // namespace ana
586 :
587 : #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */
|