testsuite: Revert to the original version of pr100056.c
[official-gcc.git] / gcc / analyzer / infinite-recursion.cc
blob42f87edee9ad67345992e73d7f86224b2e639c2b
1 /* Detection of infinite recursion.
2 Copyright (C) 2022-2025 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #define INCLUDE_VECTOR
23 #include "system.h"
24 #include "coretypes.h"
25 #include "tree.h"
26 #include "fold-const.h"
27 #include "gcc-rich-location.h"
28 #include "alloc-pool.h"
29 #include "fibonacci_heap.h"
30 #include "shortest-paths.h"
31 #include "diagnostic-core.h"
32 #include "diagnostic-event-id.h"
33 #include "diagnostic-path.h"
34 #include "function.h"
35 #include "pretty-print.h"
36 #include "sbitmap.h"
37 #include "bitmap.h"
38 #include "tristate.h"
39 #include "ordered-hash-map.h"
40 #include "selftest.h"
41 #include "json.h"
42 #include "analyzer/analyzer.h"
43 #include "analyzer/analyzer-logging.h"
44 #include "analyzer/call-string.h"
45 #include "analyzer/program-point.h"
46 #include "analyzer/store.h"
47 #include "analyzer/region-model.h"
48 #include "analyzer/constraint-manager.h"
49 #include "analyzer/sm.h"
50 #include "analyzer/pending-diagnostic.h"
51 #include "analyzer/diagnostic-manager.h"
52 #include "cfg.h"
53 #include "basic-block.h"
54 #include "gimple.h"
55 #include "gimple-iterator.h"
56 #include "gimple-pretty-print.h"
57 #include "cgraph.h"
58 #include "digraph.h"
59 #include "analyzer/supergraph.h"
60 #include "analyzer/program-state.h"
61 #include "analyzer/exploded-graph.h"
62 #include "make-unique.h"
63 #include "analyzer/checker-path.h"
64 #include "analyzer/feasible-graph.h"
65 #include "diagnostic-format-sarif.h"
67 /* A subclass of pending_diagnostic for complaining about suspected
68 infinite recursion. */
70 class infinite_recursion_diagnostic
71 : public pending_diagnostic_subclass<infinite_recursion_diagnostic>
73 public:
74 infinite_recursion_diagnostic (const exploded_node *prev_entry_enode,
75 const exploded_node *new_entry_enode,
76 tree callee_fndecl)
77 : m_prev_entry_enode (prev_entry_enode),
78 m_new_entry_enode (new_entry_enode),
79 m_callee_fndecl (callee_fndecl),
80 m_prev_entry_event (NULL)
83 const char *get_kind () const final override
85 return "infinite_recursion_diagnostic";
88 bool operator== (const infinite_recursion_diagnostic &other) const
90 return m_callee_fndecl == other.m_callee_fndecl;
93 int get_controlling_option () const final override
95 return OPT_Wanalyzer_infinite_recursion;
98 bool emit (diagnostic_emission_context &ctxt) final override
100 /* "CWE-674: Uncontrolled Recursion". */
101 ctxt.add_cwe (674);
102 return ctxt.warn ("infinite recursion");
105 bool
106 describe_final_event (pretty_printer &pp,
107 const evdesc::final_event &) final override
109 const int frames_consumed = (m_new_entry_enode->get_stack_depth ()
110 - m_prev_entry_enode->get_stack_depth ());
111 if (frames_consumed > 1)
112 pp_printf (&pp,
113 "apparently infinite chain of mutually-recursive function"
114 " calls, consuming %i stack frames per recursion",
115 frames_consumed);
116 else
117 pp_string (&pp, "apparently infinite recursion");
118 return true;
121 void
122 add_function_entry_event (const exploded_edge &eedge,
123 checker_path *emission_path) final override
125 /* Subclass of function_entry_event for use when reporting both
126 the initial and subsequent entries to the function of interest,
127 allowing for cross-referencing the first event in the description
128 of the second. */
129 class recursive_function_entry_event : public function_entry_event
131 public:
132 recursive_function_entry_event (const program_point &dst_point,
133 const infinite_recursion_diagnostic &pd,
134 bool topmost)
135 : function_entry_event (dst_point),
136 m_pd (pd),
137 m_topmost (topmost)
141 void
142 print_desc (pretty_printer &pp) const final override
144 if (m_topmost)
146 if (m_pd.m_prev_entry_event
147 && m_pd.m_prev_entry_event->get_id_ptr ()->known_p ())
148 pp_printf (&pp,
149 "recursive entry to %qE; previously entered at %@",
150 m_effective_fndecl,
151 m_pd.m_prev_entry_event->get_id_ptr ());
152 else
153 pp_printf (&pp,
154 "recursive entry to %qE",
155 m_effective_fndecl);
157 else
158 pp_printf (&pp,
159 "initial entry to %qE",
160 m_effective_fndecl);
163 private:
164 const infinite_recursion_diagnostic &m_pd;
165 bool m_topmost;
167 const exploded_node *dst_node = eedge.m_dest;
168 const program_point &dst_point = dst_node->get_point ();
169 if (eedge.m_dest == m_prev_entry_enode)
171 gcc_assert (m_prev_entry_event == NULL);
172 std::unique_ptr<checker_event> prev_entry_event
173 = make_unique <recursive_function_entry_event> (dst_point,
174 *this, false);
175 m_prev_entry_event = prev_entry_event.get ();
176 emission_path->add_event (std::move (prev_entry_event));
178 else if (eedge.m_dest == m_new_entry_enode)
179 emission_path->add_event
180 (make_unique<recursive_function_entry_event> (dst_point, *this, true));
181 else
182 pending_diagnostic::add_function_entry_event (eedge, emission_path);
185 /* Customize the location where the warning_event appears, putting
186 it at the topmost entrypoint to the function. */
187 void add_final_event (const state_machine *,
188 const exploded_node *enode,
189 const event_loc_info &,
190 tree,
191 state_machine::state_t,
192 checker_path *emission_path) final override
194 gcc_assert (m_new_entry_enode);
195 emission_path->add_event
196 (make_unique<warning_event>
197 (event_loc_info (m_new_entry_enode->get_supernode
198 ()->get_start_location (),
199 m_callee_fndecl,
200 m_new_entry_enode->get_stack_depth ()),
201 enode,
202 nullptr, nullptr, nullptr));
205 /* Reject paths in which conjured svalues have affected control flow
206 since m_prev_entry_enode. */
208 bool check_valid_fpath_p (const feasible_node &final_fnode,
209 const gimple *)
210 const final override
212 /* Reject paths in which calls with unknown side effects have occurred
213 since m_prev_entry_enode.
214 Find num calls with side effects. Walk backward until we reach the
215 pref */
216 gcc_assert (final_fnode.get_inner_node () == m_new_entry_enode);
218 /* FG is actually a tree. Walk backwards from FINAL_FNODE until we
219 reach the prev_entry_enode (or the origin). */
220 const feasible_node *iter_fnode = &final_fnode;
221 while (iter_fnode->get_inner_node ()->m_index != 0)
223 gcc_assert (iter_fnode->m_preds.length () == 1);
225 feasible_edge *pred_fedge
226 = static_cast <feasible_edge *> (iter_fnode->m_preds[0]);
228 /* Determine if conjured svalues have affected control flow
229 since the prev entry node. */
230 if (fedge_uses_conjured_svalue_p (pred_fedge))
231 /* If so, then reject this diagnostic. */
232 return false;
233 iter_fnode = static_cast <feasible_node *> (pred_fedge->m_src);
234 if (iter_fnode->get_inner_node () == m_prev_entry_enode)
235 /* Accept this diagnostic. */
236 return true;
239 /* We shouldn't get here; if we do, reject the diagnostic. */
240 gcc_unreachable ();
241 return false;
244 void maybe_add_sarif_properties (sarif_object &result_obj)
245 const final override
247 sarif_property_bag &props = result_obj.get_or_create_properties ();
248 #define PROPERTY_PREFIX "gcc/analyzer/infinite_recursion_diagnostic/"
249 props.set_integer (PROPERTY_PREFIX "prev_entry_enode",
250 m_prev_entry_enode->m_index);
251 props.set_integer (PROPERTY_PREFIX "new_entry_enode",
252 m_new_entry_enode->m_index);
253 #undef PROPERTY_PREFIX
256 private:
257 /* Return true iff control flow along FEDGE was affected by
258 a conjured_svalue. */
259 static bool fedge_uses_conjured_svalue_p (feasible_edge *fedge)
261 const exploded_edge *eedge = fedge->get_inner_edge ();
262 const superedge *sedge = eedge->m_sedge;
263 if (!sedge)
264 return false;
265 const cfg_superedge *cfg_sedge = sedge->dyn_cast_cfg_superedge ();
266 if (!cfg_sedge)
267 return false;
268 const gimple *last_stmt = sedge->m_src->get_last_stmt ();
269 if (!last_stmt)
270 return false;
272 const feasible_node *dst_fnode
273 = static_cast<const feasible_node *> (fedge->m_dest);
274 const region_model &model = dst_fnode->get_state ().get_model ();
276 if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
278 if (expr_uses_conjured_svalue_p (model, gimple_cond_lhs (cond_stmt)))
279 return true;
280 if (expr_uses_conjured_svalue_p (model, gimple_cond_rhs (cond_stmt)))
281 return true;
283 else if (const gswitch *switch_stmt
284 = dyn_cast <const gswitch *> (last_stmt))
286 if (expr_uses_conjured_svalue_p (model,
287 gimple_switch_index (switch_stmt)))
288 return true;
290 return false;
293 /* Return true iff EXPR is affected by a conjured_svalue. */
294 static bool expr_uses_conjured_svalue_p (const region_model &model,
295 tree expr)
297 class conjured_svalue_finder : public visitor
299 public:
300 conjured_svalue_finder () : m_found_conjured_svalues (false)
303 void
304 visit_conjured_svalue (const conjured_svalue *) final override
306 m_found_conjured_svalues = true;
309 bool m_found_conjured_svalues;
312 const svalue *sval = model.get_rvalue (expr, NULL);
313 conjured_svalue_finder v;
314 sval->accept (&v);
315 return v.m_found_conjured_svalues;
318 const exploded_node *m_prev_entry_enode;
319 const exploded_node *m_new_entry_enode;
320 tree m_callee_fndecl;
321 const checker_event *m_prev_entry_event;
324 /* Return true iff ENODE is the PK_BEFORE_SUPERNODE at a function
325 entrypoint. */
327 static bool
328 is_entrypoint_p (exploded_node *enode)
330 /* Look for an entrypoint to a function... */
331 const supernode *snode = enode->get_supernode ();
332 if (!snode)
333 return false;
334 if (!snode->entry_p ())
335 return false;;
336 const program_point &point = enode->get_point ();
337 if (point.get_kind () != PK_BEFORE_SUPERNODE)
338 return false;
339 return true;
342 /* Walk backwards through the eg, looking for the first
343 enode we find that's also the entrypoint of the same function. */
345 exploded_node *
346 exploded_graph::find_previous_entry_to (function *top_of_stack_fun,
347 exploded_node *enode) const
349 auto_vec<exploded_node *> worklist;
350 hash_set<exploded_node *> visited;
352 visited.add (enode);
353 for (auto in_edge : enode->m_preds)
354 worklist.safe_push (in_edge->m_src);
356 while (worklist.length () > 0)
358 exploded_node *iter = worklist.pop ();
360 if (is_entrypoint_p (iter)
361 && iter->get_function () == top_of_stack_fun)
362 return iter;
364 if (visited.contains (iter))
365 continue;
366 visited.add (iter);
367 for (auto in_edge : iter->m_preds)
368 worklist.safe_push (in_edge->m_src);
371 /* Not found. */
372 return NULL;
375 /* Given BASE_REG within ENCLOSING_FRAME (such as a function parameter),
376 remap it to the equivalent region within EQUIV_PREV_FRAME.
378 For example, given param "n" within frame "foo@3", and equiv prev frame
379 "foo@1", remap it to param "n" within frame "foo@1". */
381 static const region *
382 remap_enclosing_frame (const region *base_reg,
383 const frame_region *enclosing_frame,
384 const frame_region *equiv_prev_frame,
385 region_model_manager *mgr)
387 gcc_assert (base_reg->get_parent_region () == enclosing_frame);
388 switch (base_reg->get_kind ())
390 default:
391 /* We should only encounter params and varargs at the topmost
392 entrypoint. */
393 gcc_unreachable ();
395 case RK_VAR_ARG:
397 const var_arg_region *var_arg_reg = (const var_arg_region *)base_reg;
398 return mgr->get_var_arg_region (equiv_prev_frame,
399 var_arg_reg->get_index ());
401 case RK_DECL:
403 const decl_region *decl_reg = (const decl_region *)base_reg;
404 return equiv_prev_frame->get_region_for_local (mgr,
405 decl_reg->get_decl (),
406 NULL);
411 /* Return true iff SVAL is unknown, or contains an unknown svalue. */
413 static bool
414 contains_unknown_p (const svalue *sval)
416 if (sval->get_kind () == SK_UNKNOWN)
417 return true;
418 if (const compound_svalue *compound_sval
419 = sval->dyn_cast_compound_svalue ())
420 for (auto iter : *compound_sval)
421 if (iter.second->get_kind () == SK_UNKNOWN)
422 return true;
423 return false;
426 /* Subroutine of sufficiently_different_p. Compare the store bindings
427 for BASE_REG within NEW_ENTRY_ENODE and PREV_ENTRY_ENODE.
429 Return true if the state of NEW_ENTRY_ENODE is sufficiently different
430 from PREV_ENTRY_ENODE within BASE_REG to suggest that some variant is
431 being modified, and thus the recursion isn't infinite.
433 Return false if the states for BASE_REG are effectively the same. */
435 static bool
436 sufficiently_different_region_binding_p (exploded_node *new_entry_enode,
437 exploded_node *prev_entry_enode,
438 const region *base_reg)
440 /* Compare the stores of the two enodes. */
441 const region_model &new_model
442 = *new_entry_enode->get_state ().m_region_model;
443 const region_model &prev_model
444 = *prev_entry_enode->get_state ().m_region_model;
446 /* Get the value within the new frame. */
447 const svalue *new_sval
448 = new_model.get_store_value (base_reg, NULL);
450 /* If any part of the value is UNKNOWN (e.g. due to hitting
451 complexity limits) assume that it differs from the previous
452 value. */
453 if (contains_unknown_p (new_sval))
454 return true;
456 /* Get the equivalent value within the old enode. */
457 const svalue *prev_sval;
459 if (const frame_region *enclosing_frame
460 = base_reg->maybe_get_frame_region ())
462 /* We have a binding within a frame in the new entry enode. */
464 /* Consider changes in bindings below the original entry
465 to the recursion. */
466 const int old_stack_depth = prev_entry_enode->get_stack_depth ();
467 if (enclosing_frame->get_stack_depth () < old_stack_depth)
468 prev_sval = prev_model.get_store_value (base_reg, NULL);
469 else
471 /* Ignore bindings within frames below the new entry node. */
472 const int new_stack_depth = new_entry_enode->get_stack_depth ();
473 if (enclosing_frame->get_stack_depth () < new_stack_depth)
474 return false;
476 /* We have a binding within the frame of the new entry node,
477 presumably a parameter. */
479 /* Get the value within the equivalent frame of
480 the old entrypoint; typically will be the initial_svalue
481 of the parameter. */
482 const frame_region *equiv_prev_frame
483 = prev_model.get_current_frame ();
484 const region *equiv_prev_base_reg
485 = remap_enclosing_frame (base_reg,
486 enclosing_frame,
487 equiv_prev_frame,
488 new_model.get_manager ());
489 prev_sval
490 = prev_model.get_store_value (equiv_prev_base_reg, NULL);
493 else
494 prev_sval = prev_model.get_store_value (base_reg, NULL);
496 /* If the prev_sval contains UNKNOWN (e.g. due to hitting complexity limits)
497 assume that it will differ from any new value. */
498 if (contains_unknown_p (prev_sval))
499 return true;
501 if (new_sval != prev_sval)
502 return true;
504 return false;
507 /* Compare the state of memory at NEW_ENTRY_ENODE and PREV_ENTRY_ENODE,
508 both of which are entrypoints to the same function, where recursion has
509 occurred.
511 Return true if the state of NEW_ENTRY_ENODE is sufficiently different
512 from PREV_ENTRY_ENODE to suggest that some variant is being modified,
513 and thus the recursion isn't infinite.
515 Return false if the states are effectively the same, suggesting that
516 the recursion is infinite.
518 For example, consider mutually recursive functions "foo" and "bar".
519 At the entrypoint to a "foo" frame where we've detected recursion,
520 we might have three frames on the stack: the new 'foo'@3, an inner
521 'bar'@2, and the innermost 'foo'@1.
523 (gdb) call enode->dump(m_ext_state)
524 EN: 16
525 callstring: [(SN: 9 -> SN: 3 in foo), (SN: 5 -> SN: 8 in bar)]
526 before SN: 0 (NULL from-edge)
528 rmodel:
529 stack depth: 3
530 frame (index 2): frame: ‘foo’@3
531 frame (index 1): frame: ‘bar’@2
532 frame (index 0): frame: ‘foo’@1
533 clusters within root region
534 cluster for: (*INIT_VAL(f_4(D)))
535 clusters within frame: ‘bar’@2
536 cluster for: b_2(D): INIT_VAL(f_4(D))
537 clusters within frame: ‘foo’@3
538 cluster for: f_4(D): INIT_VAL(f_4(D))
539 m_called_unknown_fn: FALSE
541 whereas for the previous entry node we'd have just the innermost
542 'foo'@1
544 (gdb) call prev_entry_enode->dump(m_ext_state)
545 EN: 1
546 callstring: []
547 before SN: 0 (NULL from-edge)
549 rmodel:
550 stack depth: 1
551 frame (index 0): frame: ‘foo’@1
552 clusters within root region
553 cluster for: (*INIT_VAL(f_4(D)))
554 m_called_unknown_fn: FALSE
556 We want to abstract away frames 1 and 2 in the new entry enode,
557 and compare its frame 3 with the frame 1 in the previous entry
558 enode, and determine if enough state changes between them to
559 rule out infinite recursion. */
561 static bool
562 sufficiently_different_p (exploded_node *new_entry_enode,
563 exploded_node *prev_entry_enode,
564 logger *logger)
566 LOG_SCOPE (logger);
567 gcc_assert (new_entry_enode);
568 gcc_assert (prev_entry_enode);
569 gcc_assert (is_entrypoint_p (new_entry_enode));
570 gcc_assert (is_entrypoint_p (prev_entry_enode));
572 /* Compare the stores of the two enodes. */
573 const region_model &new_model
574 = *new_entry_enode->get_state ().m_region_model;
575 const store &new_store = *new_model.get_store ();
577 for (auto kv : new_store)
579 const region *base_reg = kv.first;
580 if (sufficiently_different_region_binding_p (new_entry_enode,
581 prev_entry_enode,
582 base_reg))
583 return true;
586 /* No significant differences found. */
587 return false;
590 /* Implementation of -Wanalyzer-infinite-recursion.
592 Called when adding ENODE to the graph, after adding its first in-edge.
594 For function entrypoints, see if recursion has occurred, and, if so,
595 check if the state of memory changed between the recursion levels,
596 which would suggest some kind of decreasing variant that leads to
597 termination.
599 For recursive calls where the state of memory is effectively unchanged
600 between recursion levels, warn with -Wanalyzer-infinite-recursion. */
602 void
603 exploded_graph::detect_infinite_recursion (exploded_node *enode)
605 if (!is_entrypoint_p (enode))
606 return;
607 function *top_of_stack_fun = enode->get_function ();
608 gcc_assert (top_of_stack_fun);
610 /* ....where a call to that function is already in the call string. */
611 const call_string &call_string = enode->get_point ().get_call_string ();
613 if (call_string.count_occurrences_of_function (top_of_stack_fun) < 2)
614 return;
616 tree fndecl = top_of_stack_fun->decl;
618 log_scope s (get_logger (),
619 "checking for infinite recursion",
620 "considering recursion at EN: %i entering %qE",
621 enode->m_index, fndecl);
623 /* Find enode that's the entrypoint for the previous frame for fndecl
624 in the recursion. */
625 exploded_node *prev_entry_enode
626 = find_previous_entry_to (top_of_stack_fun, enode);
627 gcc_assert (prev_entry_enode);
628 if (get_logger ())
629 get_logger ()->log ("previous entrypoint to %qE is EN: %i",
630 fndecl, prev_entry_enode->m_index);
632 /* Look for changes to the state of memory between the recursion levels. */
633 if (sufficiently_different_p (enode, prev_entry_enode, get_logger ()))
634 return;
636 /* Otherwise, the state of memory is effectively the same between the two
637 recursion levels; warn. */
639 const supernode *caller_snode = call_string.get_top_of_stack ().m_caller;
640 const supernode *snode = enode->get_supernode ();
641 gcc_assert (caller_snode->m_returning_call);
642 pending_location ploc (enode,
643 snode,
644 caller_snode->m_returning_call,
645 nullptr);
646 get_diagnostic_manager ().add_diagnostic
647 (ploc,
648 make_unique<infinite_recursion_diagnostic> (prev_entry_enode,
649 enode,
650 fndecl));