aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/infinite-recursion.cc
AgeCommit message (Collapse)AuthorFilesLines
2023-06-21analyzer: add text-art visualizations of out-of-bounds accesses [PR106626]David Malcolm1-1/+1
This patch extends -Wanalyzer-out-of-bounds so that, where possible, it will emit a text art diagram visualizing the spatial relationship between (a) the memory region that the analyzer predicts would be accessed, versus (b) the range of memory that is valid to access - whether they overlap, are touching, are close or far apart; which one is before or after in memory, the relative sizes involved, the direction of the access (read vs write), and, in some cases, the values of data involved. This diagram can be suppressed using -fdiagnostics-text-art-charset=none. For example, given: int32_t arr[10]; int32_t int_arr_read_element_before_start_far(void) { return arr[-100]; } it emits: demo-1.c: In function ‘int_arr_read_element_before_start_far’: demo-1.c:7:13: warning: buffer under-read [CWE-127] [-Wanalyzer-out-of-bounds] 7 | return arr[-100]; | ~~~^~~~~~ ‘int_arr_read_element_before_start_far’: event 1 | | 7 | return arr[-100]; | | ~~~^~~~~~ | | | | | (1) out-of-bounds read from byte -400 till byte -397 but ‘arr’ starts at byte 0 | demo-1.c:7:13: note: valid subscripts for ‘arr’ are ‘[0]’ to ‘[9]’ ┌───────────────────────────┐ │read of ‘int32_t’ (4 bytes)│ └───────────────────────────┘ ^ │ │ ┌───────────────────────────┐ ┌────────┬────────┬─────────┐ │ │ │ [0] │ ... │ [9] │ │ before valid range │ ├────────┴────────┴─────────┤ │ │ │‘arr’ (type: ‘int32_t[10]’)│ └───────────────────────────┘ └───────────────────────────┘ ├─────────────┬─────────────┤├─────┬──────┤├─────────────┬─────────────┤ │ │ │ ╭────────────┴───────────╮ ╭────┴────╮ ╭───────┴──────╮ │⚠️ under-read of 4 bytes│ │396 bytes│ │size: 40 bytes│ ╰────────────────────────╯ ╰─────────╯ ╰──────────────╯ and given: #include <string.h> void test_non_ascii () { char buf[5]; strcpy (buf, "文字化け"); } it emits: demo-2.c: In function ‘test_non_ascii’: demo-2.c:7:3: warning: stack-based buffer overflow [CWE-121] [-Wanalyzer-out-of-bounds] 7 | strcpy (buf, "文字化け"); | ^~~~~~~~~~~~~~~~~~~~~~~~ ‘test_non_ascii’: events 1-2 | | 6 | char buf[5]; | | ^~~ | | | | | (1) capacity: 5 bytes | 7 | strcpy (buf, "文字化け"); | | ~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (2) out-of-bounds write from byte 5 till byte 12 but ‘buf’ ends at byte 5 | demo-2.c:7:3: note: write of 8 bytes to beyond the end of ‘buf’ 7 | strcpy (buf, "文字化け"); | ^~~~~~~~~~~~~~~~~~~~~~~~ demo-2.c:7:3: note: valid subscripts for ‘buf’ are ‘[0]’ to ‘[4]’ ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐ │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │ ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤ │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │ ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤ │ U+6587 │ U+5b57 │ U+5316 │ U+3051 │U+0000│ ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤ │ 文 │ 字 │ 化 │ け │ NUL │ ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤ │ string literal (type: ‘char[13]’) │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ v v v v v v v v v v v v v ┌─────┬────────────────┬────┐┌─────────────────────────────────────────┐ │ [0] │ ... │[4] ││ │ ├─────┴────────────────┴────┤│ after valid range │ │ ‘buf’ (type: ‘char[5]’) ││ │ └───────────────────────────┘└─────────────────────────────────────────┘ ├─────────────┬─────────────┤├────────────────────┬────────────────────┤ │ │ ╭────────┴────────╮ ╭───────────┴──────────╮ │capacity: 5 bytes│ │⚠️ overflow of 8 bytes│ ╰─────────────────╯ ╰──────────────────────╯ showing that the overflow occurs partway through the UTF-8 encoding of the U+5b57 code point. There are lots more examples in the test suite. It doesn't show up in this email, but the above diagrams are colorized to constrast the valid and invalid access ranges. gcc/ChangeLog: PR analyzer/106626 * Makefile.in (ANALYZER_OBJS): Add analyzer/access-diagram.o. * doc/invoke.texi (Wanalyzer-out-of-bounds): Add description of text art. (fanalyzer-debug-text-art): New. gcc/analyzer/ChangeLog: PR analyzer/106626 * access-diagram.cc: New file. * access-diagram.h: New file. * analyzer.h (class region_offset): Add default ctor. (region_offset::make_byte_offset): New decl. (region_offset::concrete_p): New. (region_offset::get_concrete_byte_offset): New. (region_offset::calc_symbolic_bit_offset): New decl. (region_offset::calc_symbolic_byte_offset): New decl. (region_offset::dump_to_pp): New decl. (region_offset::dump): New decl. (operator<, operator<=, operator>, operator>=): New decls for region_offset. * analyzer.opt (-param=analyzer-text-art-string-ellipsis-threshold=): New. (-param=analyzer-text-art-string-ellipsis-head-len=): New. (-param=analyzer-text-art-string-ellipsis-tail-len=): New. (-param=analyzer-text-art-ideal-canvas-width=): New. (fanalyzer-debug-text-art): New. * bounds-checking.cc: Include "intl.h", "diagnostic-diagram.h", and "analyzer/access-diagram.h". (class out_of_bounds::oob_region_creation_event_capacity): New. (out_of_bounds::out_of_bounds): Add "model" and "sval_hint" params. (out_of_bounds::mark_interesting_stuff): Use the base region. (out_of_bounds::add_region_creation_events): Use oob_region_creation_event_capacity. (out_of_bounds::get_dir): New pure vfunc. (out_of_bounds::maybe_show_notes): New. (out_of_bounds::maybe_show_diagram): New. (out_of_bounds::make_access_diagram): New. (out_of_bounds::m_model): New field. (out_of_bounds::m_sval_hint): New field. (out_of_bounds::m_region_creation_event_id): New field. (concrete_out_of_bounds::concrete_out_of_bounds): Update for new fields. (concrete_past_the_end::concrete_past_the_end): Likewise. (concrete_past_the_end::add_region_creation_events): Use oob_region_creation_event_capacity. (concrete_buffer_overflow::concrete_buffer_overflow): Update for new fields. (concrete_buffer_overflow::emit): Replace call to maybe_describe_array_bounds with maybe_show_notes. (concrete_buffer_overflow::get_dir): New. (concrete_buffer_over_read::concrete_buffer_over_read): Update for new fields. (concrete_buffer_over_read::emit): Replace call to maybe_describe_array_bounds with maybe_show_notes. (concrete_buffer_overflow::get_dir): New. (concrete_buffer_underwrite::concrete_buffer_underwrite): Update for new fields. (concrete_buffer_underwrite::emit): Replace call to maybe_describe_array_bounds with maybe_show_notes. (concrete_buffer_underwrite::get_dir): New. (concrete_buffer_under_read::concrete_buffer_under_read): Update for new fields. (concrete_buffer_under_read::emit): Replace call to maybe_describe_array_bounds with maybe_show_notes. (concrete_buffer_under_read::get_dir): New. (symbolic_past_the_end::symbolic_past_the_end): Update for new fields. (symbolic_buffer_overflow::symbolic_buffer_overflow): Likewise. (symbolic_buffer_overflow::emit): Call maybe_show_notes. (symbolic_buffer_overflow::get_dir): New. (symbolic_buffer_over_read::symbolic_buffer_over_read): Update for new fields. (symbolic_buffer_over_read::emit): Call maybe_show_notes. (symbolic_buffer_over_read::get_dir): New. (region_model::check_symbolic_bounds): Add "sval_hint" param. Pass it and sized_offset_reg to diagnostics. (region_model::check_region_bounds): Add "sval_hint" param, passing it to diagnostics. * diagnostic-manager.cc (diagnostic_manager::emit_saved_diagnostic): Pass logger to pending_diagnostic::emit. * engine.cc: Add logger param to pending_diagnostic::emit implementations. * infinite-recursion.cc: Likewise. * kf-analyzer.cc: Likewise. * kf.cc: Likewise. Add nullptr for new param of check_region_for_write. * pending-diagnostic.h: Likewise in decl. * region-model-manager.cc (region_model_manager::get_or_create_int_cst): Convert param from poly_int64 to const poly_wide_int_ref &. (region_model_manager::maybe_fold_binop): Support type being NULL when checking for floating-point types. Check for (X + Y) - X => Y. Be less strict about types when folding associative ops. Check for (X + Y) * CST => (X * CST) + (Y * CST). * region-model-manager.h (region_model_manager::get_or_create_int_cst): Convert param from poly_int64 to const poly_wide_int_ref &. * region-model.cc: Add logger param to pending_diagnostic::emit implementations. (region_model::check_external_function_for_access_attr): Update for new param of check_region_for_write. (region_model::deref_rvalue): Use nullptr rather than NULL. (region_model::get_capacity): Handle RK_STRING. (region_model::check_region_access): Add "sval_hint" param; pass it to check_region_bounds. (region_model::check_region_for_write): Add "sval_hint" param; pass it to check_region_access. (region_model::check_region_for_read): Add NULL for new param to check_region_access. (region_model::set_value): Pass rhs_sval to check_region_for_write. (region_model::get_representative_path_var_1): Handle SK_CONSTANT in the check for infinite recursion. * region-model.h (region_model::check_region_for_write): Add "sval_hint" param. (region_model::check_region_access): Likewise. (region_model::check_symbolic_bounds): Likewise. (region_model::check_region_bounds): Likewise. * region.cc (region_offset::make_byte_offset): New. (region_offset::calc_symbolic_bit_offset): New. (region_offset::calc_symbolic_byte_offset): New. (region_offset::dump_to_pp): New. (region_offset::dump): New. (struct linear_op): New. (operator<, operator<=, operator>, operator>=): New, for region_offset. (region::get_next_offset): New. (region::get_relative_symbolic_offset): Use ptrdiff_type_node. (field_region::get_relative_symbolic_offset): Likewise. (element_region::get_relative_symbolic_offset): Likewise. (bit_range_region::get_relative_symbolic_offset): Likewise. * region.h (region::get_next_offset): New decl. * sm-fd.cc: Add logger param to pending_diagnostic::emit implementations. * sm-file.cc: Likewise. * sm-malloc.cc: Likewise. * sm-pattern-test.cc: Likewise. * sm-sensitive.cc: Likewise. * sm-signal.cc: Likewise. * sm-taint.cc: Likewise. * store.cc (bit_range::contains_p): Allow "out" to be null. * store.h (byte_range::get_start_bit_offset): New. (byte_range::get_next_bit_offset): New. * varargs.cc: Add logger param to pending_diagnostic::emit implementations. gcc/testsuite/ChangeLog: PR analyzer/106626 * gcc.dg/analyzer/data-model-1.c (test_16): Update for out-of-bounds working. * gcc.dg/analyzer/out-of-bounds-diagram-1-ascii.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-1-debug.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-1-emoji.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-1-json.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-1-sarif.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-1-unicode.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-10.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-11.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-12.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-13.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-14.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-15.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-2.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-3.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-4.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-5-ascii.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-5-unicode.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-6.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-7.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-8.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-9.c: New test. * gcc.dg/analyzer/pattern-test-2.c: Update expected results. * gcc.dg/analyzer/pr101962.c: Update expected results. * gcc.dg/plugin/analyzer_gil_plugin.c: Add logger param to pending_diagnostic::emit implementations. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2023-03-01analyzer: fix infinite recursion false +ves [PR108935]David Malcolm1-51/+100
gcc/analyzer/ChangeLog: PR analyzer/108935 * infinite-recursion.cc (contains_unknown_p): New. (sufficiently_different_region_binding_p): New function, splitting out inner loop from... (sufficiently_different_p): ...here. Extend detection of unknown svalues to also include svalues that contain unknown. Treat changes in frames below the entry to the recursion as being sufficiently different to reject being an infinite recursion. gcc/testsuite/ChangeLog: PR analyzer/108935 * gcc.dg/analyzer/infinite-recursion-pr108935-1.c: New test. * gcc.dg/analyzer/infinite-recursion-pr108935-1a.c: New test. * gcc.dg/analyzer/infinite-recursion-pr108935-2.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2023-02-15analyzer: fix uninit false +ves [PR108664,PR108666,PR108725]David Malcolm1-3/+4
This patch updates poisoned_value_diagnostic so that, where possible, it checks to see if the value is still poisoned along the execution path seen during feasibility analysis, rather than just that seen in the exploded graph. Integration testing shows this reduction in the number of false positives: -Wanalyzer-use-of-uninitialized-value: 191 -> 153 (-38) where the changes happen in: coreutils-9.1: 34 -> 20 (-14) qemu-7.2.0: 78 -> 54 (-24) gcc/analyzer/ChangeLog: PR analyzer/108664 PR analyzer/108666 PR analyzer/108725 * diagnostic-manager.cc (epath_finder::get_best_epath): Add "target_stmt" param. (epath_finder::explore_feasible_paths): Likewise. (epath_finder::process_worklist_item): Likewise. (saved_diagnostic::calc_best_epath): Pass m_stmt to epath_finder::get_best_epath. * engine.cc (feasibility_state::maybe_update_for_edge): Move per-stmt logic to... (feasibility_state::update_for_stmt): ...this new function. * exploded-graph.h (feasibility_state::update_for_stmt): New decl. * feasible-graph.cc (feasible_node::get_state_at_stmt): New. * feasible-graph.h: Include "analyzer/exploded-graph.h". (feasible_node::get_state_at_stmt): New decl. * infinite-recursion.cc (infinite_recursion_diagnostic::check_valid_fpath_p): Update for vfunc signature change. * pending-diagnostic.h (pending_diagnostic::check_valid_fpath_p): Convert first param to a reference. Add stmt param. * region-model.cc: Include "analyzer/feasible-graph.h". (poisoned_value_diagnostic::poisoned_value_diagnostic): Add "check_expr" param. (poisoned_value_diagnostic::check_valid_fpath_p): New. (poisoned_value_diagnostic::m_check_expr): New field. (region_model::check_for_poison): Attempt to supply a check_expr to the diagnostic (region_model::deref_rvalue): Add NULL for new check_expr param of poisoned_value_diagnostic. (region_model::get_or_create_region_for_heap_alloc): Don't reuse regions that are marked as TOUCHED. gcc/testsuite/ChangeLog: PR analyzer/108664 PR analyzer/108666 PR analyzer/108725 * gcc.dg/analyzer/coreutils-cksum-pr108664.c: New test. * gcc.dg/analyzer/coreutils-sum-pr108666.c: New test. * gcc.dg/analyzer/torture/uninit-pr108725.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2023-01-26analyzer: fix false positives from -Wanalyzer-infinite-recursion [PR108524]David Malcolm1-0/+100
Reject -Wanalyzer-infinite-recursion diagnostics in which control flow has been affected by conjured_svalues between the initial call to a function and the subsequent entry to that function. This prevents false positives such as in qemu's recursive JSON parser where function calls are changing state in the rest of the program (e.g. consuming tokens), despite the modelled state being effectively identical at both nested entrypoints. gcc/analyzer/ChangeLog: PR analyzer/108524 * analyzer.h (class feasible_node): New forward decl. * diagnostic-manager.cc (epath_finder::get_best_epath): Add "pd" param. (epath_finder::explore_feasible_paths): Likewise. (epath_finder::process_worklist_item): Likewise. Use it to call pending_diagnostic::check_valid_fpath_p on the final fpath to give pending_diagnostic a way to add additional restrictions on feasibility. (saved_diagnostic::calc_best_epath): Pass pending_diagnostic to epath_finder::get_best_epath. * infinite-recursion.cc: Include "analyzer/feasible-graph.h". (infinite_recursion_diagnostic::check_valid_fpath_p): New. (infinite_recursion_diagnostic::fedge_uses_conjured_svalue_p): New. (infinite_recursion_diagnostic::expr_uses_conjured_svalue_p): New. * pending-diagnostic.h (pending_diagnostic::check_valid_fpath_p): New vfunc. gcc/testsuite/ChangeLog: PR analyzer/108524 * gcc.dg/analyzer/infinite-recursion-pr108524-1.c: New test. * gcc.dg/analyzer/infinite-recursion-pr108524-2.c: New test. * gcc.dg/analyzer/infinite-recursion-pr108524-qobject-json-parser.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2023-01-19analyzer: use dominator info in -Wanalyzer-deref-before-check [PR108455]David Malcolm1-1/+2
My integration testing [1] of -fanalyzer in GCC 13 is showing a lot of diagnostics from the new -Wanalyzer-deref-before-check warning on real-world C projects, and most of these seem to be false positives. This patch updates the warning to make it much less likely to fire: - only intraprocedural cases are now reported - reject cases in which there are control flow paths to the check that didn't come through the dereference, by looking at BB dominator information. This fixes a false positive seen in git-2.39.0's pack-revindex.c: load_revindex_from_disk (PR analyzer/108455), in which a shared "cleanup:" section checks "data" for NULL, and depending on how much of the function is executed "data" might or might not have already been dereferenced. The counts of -Wanalyzer-deref-before-check diagnostics in [1] before/after this patch show this improvement: Known false positives: 6 -> 0 (-6) Known true positives: 1 -> 1 Unclassified positives: 123 -> 63 (-60) [1] https://github.com/davidmalcolm/gcc-analyzer-integration-tests gcc/analyzer/ChangeLog: PR analyzer/108455 * analyzer.h (class checker_event): New forward decl. (class state_change_event): Indent. (class warning_event): New forward decl. * checker-event.cc (state_change_event::state_change_event): Add "enode" param. (warning_event::get_desc): Update for new param of evdesc::final_event ctor. * checker-event.h (state_change_event::state_change_event): Add "enode" param. (state_change_event::get_exploded_node): New accessor. (state_change_event::m_enode): New field. (warning_event::warning_event): New "enode" param. (warning_event::get_exploded_node): New accessor. (warning_event::m_enode): New field. * diagnostic-manager.cc (state_change_event_creator::on_global_state_change): Pass src_node to state_change_event ctor. (state_change_event_creator::on_state_change): Likewise. (null_assignment_sm_context::set_next_state): Pass NULL for new param of state_change_event ctor. * infinite-recursion.cc (infinite_recursion_diagnostic::add_final_event): Update for new param of warning_event ctor. * pending-diagnostic.cc (pending_diagnostic::add_final_event): Pass enode to warning_event ctor. * pending-diagnostic.h (evdesc::final_event): Add reference to warning_event. * sm-malloc.cc: Include "analyzer/checker-event.h" and "analyzer/exploded-graph.h". (deref_before_check::deref_before_check): Initialize new fields. (deref_before_check::emit): Reject warnings in which we were unable to determine the enodes of the dereference and the check. Reject warnings interprocedural warnings. Reject warnings in which the dereference doesn't dominate the check. (deref_before_check::describe_state_change): Set m_deref_enode. (deref_before_check::describe_final_event): Set m_check_enode. (deref_before_check::m_deref_enode): New field. (deref_before_check::m_check_enode): New field. gcc/testsuite/ChangeLog: PR analyzer/108455 * gcc.dg/analyzer/deref-before-check-1.c: Add test coverage involving dominance. * gcc.dg/analyzer/deref-before-check-pr108455-1.c: New test. * gcc.dg/analyzer/deref-before-check-pr108455-git-pack-revindex.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2023-01-16Update copyright years.Jakub Jelinek1-1/+1
2022-12-02analyzer: introduce struct event_loc_infoDavid Malcolm1-3/+4
gcc/analyzer/ChangeLog: * analyzer.h (struct event_loc_info): New forward decl. * bounds-checking.cc: Use event_loc_info throughout to bundle the loc, fndecl, depth triples. * call-info.cc: Likewise. * checker-event.cc: Likewise. * checker-event.h (struct event_loc_info): New decl. Use it throughout to bundle the loc, fndecl, depth triples. * checker-path.cc: Likewise. * checker-path.h: Likewise. * diagnostic-manager.cc: Likewise. * engine.cc: Likewise. * infinite-recursion.cc: Likewise. * pending-diagnostic.cc: Likewise. * pending-diagnostic.h: Likewise. * region-model.cc: Likewise. * sm-signal.cc: Likewise. * varargs.cc: Likewise. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
2022-11-11analyzer: new warning: -Wanalyzer-infinite-recursion [PR106147]David Malcolm1-0/+481
This patch adds a new -Wanalyzer-infinite-recursion warning to -fanalyzer, which complains about certain cases of infinite recursion. Specifically, when it detects recursion during its symbolic execution of the user's code, it compares the state of memory to that at the previous level of recursion, and if nothing appears to have effectively changed, it issues a warning. Unlike the middle-end warning -Winfinite-recursion (added by Martin Sebor in GCC 12; r12-5483-g30ba058f77eedf), the analyzer warning complains if there exists an interprocedural path in which recursion occurs in which memory has not changed, whereas -Winfinite-recursion complains if *every* intraprocedural path through the function leads to a self-call. Hence the warnings complement each other: there's some overlap, but each also catches issues that the other misses. For example, the new warning complains about a guarded recursion in which the guard is passed unchanged: void test_guarded (int flag) { if (flag) test_guarded (flag); } t.c: In function 'test_guarded': t.c:4:5: warning: infinite recursion [CWE-674] [-Wanalyzer-infinite-recursion] 4 | test_guarded (flag); | ^~~~~~~~~~~~~~~~~~~ 'test_guarded': events 1-4 | | 1 | void test_guarded (int flag) | | ^~~~~~~~~~~~ | | | | | (1) initial entry to 'test_guarded' | 2 | { | 3 | if (flag) | | ~ | | | | | (2) following 'true' branch (when 'flag != 0')... | 4 | test_guarded (flag); | | ~~~~~~~~~~~~~~~~~~~ | | | | | (3) ...to here | | (4) calling 'test_guarded' from 'test_guarded' | +--> 'test_guarded': events 5-6 | | 1 | void test_guarded (int flag) | | ^~~~~~~~~~~~ | | | | | (5) recursive entry to 'test_guarded'; previously entered at (1) | | (6) apparently infinite recursion | whereas the existing warning doesn't complain, since when "flag" is false the function doesn't recurse. The new warning doesn't trigger for e.g.: void test_param_variant (int depth) { if (depth > 0) test_param_variant (depth - 1); } on the grounds that "depth" is changing, and appears to be a variant that enforces termination of the recursion. gcc/ChangeLog: PR analyzer/106147 * Makefile.in (ANALYZER_OBJS): Add analyzer/infinite-recursion.o. gcc/analyzer/ChangeLog: PR analyzer/106147 * analyzer.opt (Wanalyzer-infinite-recursion): New. * call-string.cc (call_string::count_occurrences_of_function): New. * call-string.h (call_string::count_occurrences_of_function): New decl. * checker-path.cc (function_entry_event::function_entry_event): New ctor. (checker_path::add_final_event): Delete. * checker-path.h (function_entry_event::function_entry_event): New ctor. (function_entry_event::get_desc): Drop "final". (checker_path::add_final_event): Delete. * diagnostic-manager.cc (diagnostic_manager::emit_saved_diagnostic): Create the final event via a new pending_diagnostic::add_final_event vfunc, rather than checker_path::add_final_event. (diagnostic_manager::add_events_for_eedge): Create function entry events via a new pending_diagnostic::add_function_entry_event vfunc. * engine.cc (exploded_graph::process_node): When creating a new PK_BEFORE_SUPERNODE node, call exploded_graph::detect_infinite_recursion on it after adding the in-edge. * exploded-graph.h (exploded_graph::detect_infinite_recursion): New decl. (exploded_graph::find_previous_entry_to): New decl. * infinite-recursion.cc: New file. * pending-diagnostic.cc (pending_diagnostic::add_function_entry_event): New. (pending_diagnostic::add_final_event): New. * pending-diagnostic.h (pending_diagnostic::add_function_entry_event): New vfunc. (pending_diagnostic::add_final_event): New vfunc. gcc/ChangeLog: PR analyzer/106147 * doc/gcc/gcc-command-options/options-that-control-static-analysis.rst: Add -Wanalyzer-infinite-recursion. * doc/gcc/gcc-command-options/options-to-request-or-suppress-warnings.rst (-Winfinite-recursion): Mention -Wanalyzer-infinite-recursion. gcc/testsuite/ChangeLog: PR analyzer/106147 * g++.dg/analyzer/infinite-recursion-1.C: New test. * g++.dg/analyzer/infinite-recursion-2.C: New test, copied from g++.dg/warn/Winfinite-recursion-2.C. * g++.dg/analyzer/infinite-recursion-3.C: New test, adapted from g++.dg/warn/Winfinite-recursion-3.C. * gcc.dg/analyzer/infinite-recursion-2.c: New test. * gcc.dg/analyzer/infinite-recursion-3.c: New test. * gcc.dg/analyzer/infinite-recursion-4-limited-buggy.c: New test. * gcc.dg/analyzer/infinite-recursion-4-limited.c: New test. * gcc.dg/analyzer/infinite-recursion-4-unlimited-buggy.c: New test. * gcc.dg/analyzer/infinite-recursion-4-unlimited.c: New test. * gcc.dg/analyzer/infinite-recursion-5.c: New test, adapted from gcc.dg/Winfinite-recursion.c. * gcc.dg/analyzer/infinite-recursion-alloca.c: New test. * gcc.dg/analyzer/infinite-recursion-inlining.c: New test. * gcc.dg/analyzer/infinite-recursion-multiline-1.c: New test. * gcc.dg/analyzer/infinite-recursion-multiline-2.c: New test. * gcc.dg/analyzer/infinite-recursion-variadic.c: New test. * gcc.dg/analyzer/infinite-recursion.c: Add dg-warning directives where infinite recursions occur. * gcc.dg/analyzer/malloc-ipa-12.c: Likewise. * gcc.dg/analyzer/pr105365.c: Likewise. * gcc.dg/analyzer/pr105366.c: Likewise. * gcc.dg/analyzer/pr97029.c: Likewise. Signed-off-by: David Malcolm <dmalcolm@redhat.com>