]> gcc.gnu.org Git - gcc.git/commit
analyzer: fix up paths for inlining (PR analyzer/105962)
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 15 Jun 2022 21:44:14 +0000 (17:44 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Wed, 15 Jun 2022 21:44:14 +0000 (17:44 -0400)
commit63c073199491b7ec2261d39af51c02147c2f0daf
tree6a8c8e961f0c48d3d784883a71dbe04ce10102db
parentb06b84dbca5a11615da7193d74abfb74beaaf7ac
analyzer: fix up paths for inlining (PR analyzer/105962)

-fanalyzer runs late compared to other code analysis tools, in that in
runs on the partially-optimized gimple-ssa representation.  I chose this
point to run in the hope of easy integration with LTO.

As PR analyzer/105962 notes, this means that function inlining can occur
before the -fanalyzer "sees" the user's code.  For example given:

void foo (void *p)
{
  __builtin_free (p);
}

void bar (void *q)
{
  foo (q);
  foo (q);
}

Below -O2, -fanalyzer shows the calls and returns:

inline-1.c: In function ‘foo’:
inline-1.c:3:3: warning: double-‘free’ of ‘p’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2
    |
    |    6 | void bar (void *q)
    |      |      ^~~
    |      |      |
    |      |      (1) entry to ‘bar’
    |    7 | {
    |    8 |   foo (q);
    |      |   ~~~~~~~
    |      |   |
    |      |   (2) calling ‘foo’ from ‘bar’
    |
    +--> ‘foo’: events 3-4
           |
           |    1 | void foo (void *p)
           |      |      ^~~
           |      |      |
           |      |      (3) entry to ‘foo’
           |    2 | {
           |    3 |   __builtin_free (p);
           |      |   ~~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (4) first ‘free’ here
           |
    <------+
    |
  ‘bar’: events 5-6
    |
    |    8 |   foo (q);
    |      |   ^~~~~~~
    |      |   |
    |      |   (5) returning to ‘bar’ from ‘foo’
    |    9 |   foo (q);
    |      |   ~~~~~~~
    |      |   |
    |      |   (6) passing freed pointer ‘q’ in call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: events 7-8
           |
           |    1 | void foo (void *p)
           |      |      ^~~
           |      |      |
           |      |      (7) entry to ‘foo’
           |    2 | {
           |    3 |   __builtin_free (p);
           |      |   ~~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (8) second ‘free’ here; first ‘free’ was at (4)
           |

but at -O2, -fanalyzer "sees" this gimple:

void bar (void * q)
{
  <bb 2> [local count: 1073741824]:
  __builtin_free (q_2(D));
  __builtin_free (q_2(D));
  return;
}

where "foo" has been inlined away, leading to this unhelpful output:

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2
    |
    |    3 |   __builtin_free (p);
    |      |   ^~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (1) first ‘free’ here
    |      |   (2) second ‘free’ here; first ‘free’ was at (1)

where the stack frame information in the execution path suggests that these
events are happening in "bar", in the top stack frame.

This is what the analyzer sees, but I find it hard to decipher such
output.  Hence, as a workaround for the fact that -fanalyzer runs so
late, this patch attempts to reconstruct the "true" stack frame
information, and to inject events showing inline calls, based on the
inlining chain information recorded in the location_t values for the events.

Doing so leads to this output at -O2 on the above example (with
-fdiagnostics-show-path-depths):

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2 (depth 1)
    |
    |    6 | void bar (void *q)
    |      |      ^~~
    |      |      |
    |      |      (1) entry to ‘bar’
    |    7 | {
    |    8 |   foo (q);
    |      |   ~
    |      |   |
    |      |   (2) inlined call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: event 3 (depth 2)
           |
           |    3 |   __builtin_free (p);
           |      |   ^~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (3) first ‘free’ here
           |
    <------+
    |
  ‘bar’: event 4 (depth 1)
    |
    |    9 |   foo (q);
    |      |   ^
    |      |   |
    |      |   (4) inlined call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: event 5 (depth 2)
           |
           |    3 |   __builtin_free (p);
           |      |   ^~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (5) second ‘free’ here; first ‘free’ was at (3)
           |

reconstructing the calls and returns.

The patch also adds a new option, -fno-analyzer-undo-inlining, which can
be used to disable this reconstruction, restoring the output listed
above (this time with -fdiagnostics-show-path-depths):

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2 (depth 1)
    |
    |    3 |   __builtin_free (p);
    |      |   ^~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (1) first ‘free’ here
    |      |   (2) second ‘free’ here; first ‘free’ was at (1)
    |

gcc/analyzer/ChangeLog:
PR analyzer/105962
* analyzer.opt (fanalyzer-undo-inlining): New option.
* checker-path.cc: Include "diagnostic-core.h" and
"inlining-iterator.h".
(event_kind_to_string): Handle EK_INLINED_CALL.
(class inlining_info): New class.
(checker_event::checker_event): Move here from checker-path.h.
Store original fndecl and depth, and calculate effective fndecl
and depth based on inlining information.
(checker_event::dump): Emit original depth as well as effective
depth when they differ; likewise for fndecl.
(region_creation_event::get_desc): Use m_effective_fndecl.
(inlined_call_event::get_desc): New.
(inlined_call_event::get_meaning): New.
(checker_path::inject_any_inlined_call_events): New.
* checker-path.h (enum event_kind): Add EK_INLINED_CALL.
(checker_event::checker_event): Make protected, and move
definition to checker-path.cc.
(checker_event::get_fndecl): Use effective fndecl.
(checker_event::get_stack_depth): Use effective stack depth.
(checker_event::get_logical_location): Use effective stack depth.
(checker_event::get_original_stack_depth): New.
(checker_event::m_fndecl): Rename to...
(checker_event::m_original_fndecl): ...this.
(checker_event::m_depth): Rename to...
(checker_event::m_original_depth): ...this.
(checker_event::m_effective_fndecl): New field.
(checker_event::m_effective_depth): New field.
(class inlined_call_event): New checker_event subclass.
(checker_path::inject_any_inlined_call_events): New decl.
* diagnostic-manager.cc: Include "inlining-iterator.h".
(diagnostic_manager::emit_saved_diagnostic): Call
checker_path::inject_any_inlined_call_events.
(diagnostic_manager::prune_for_sm_diagnostic): Handle
EK_INLINED_CALL.
* engine.cc (tainted_args_function_custom_event::get_desc): Use
effective fndecl.
* inlining-iterator.h: New file.

gcc/testsuite/ChangeLog:
PR analyzer/105962
* gcc.dg/analyzer/inlining-1-multiline.c: New test.
* gcc.dg/analyzer/inlining-1-no-undo.c: New test.
* gcc.dg/analyzer/inlining-1.c: New test.
* gcc.dg/analyzer/inlining-2-multiline.c: New test.
* gcc.dg/analyzer/inlining-2.c: New test.
* gcc.dg/analyzer/inlining-3-multiline.c: New test.
* gcc.dg/analyzer/inlining-3.c: New test.
* gcc.dg/analyzer/inlining-4-multiline.c: New test.
* gcc.dg/analyzer/inlining-4.c: New test.
* gcc.dg/analyzer/inlining-5-multiline.c: New test.
* gcc.dg/analyzer/inlining-5.c: New test.
* gcc.dg/analyzer/inlining-6-multiline.c: New test.
* gcc.dg/analyzer/inlining-6.c: New test.
* gcc.dg/analyzer/inlining-7-multiline.c: New test.
* gcc.dg/analyzer/inlining-7.c: New test.

gcc/ChangeLog:
PR analyzer/105962
* doc/invoke.texi: Add -fno-analyzer-undo-inlining.
* tree-diagnostic-path.cc (default_tree_diagnostic_path_printer):
Extend -fdiagnostics-path-format=separate-events so that with
-fdiagnostics-show-path-depths it prints fndecls as well as stack
depths.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
23 files changed:
gcc/analyzer/analyzer.opt
gcc/analyzer/checker-path.cc
gcc/analyzer/checker-path.h
gcc/analyzer/diagnostic-manager.cc
gcc/analyzer/engine.cc
gcc/analyzer/inlining-iterator.h [new file with mode: 0644]
gcc/doc/invoke.texi
gcc/testsuite/gcc.dg/analyzer/inlining-1-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-1-no-undo.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-2-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-3-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-4-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-5-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-6-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-7-multiline.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/inlining-7.c [new file with mode: 0644]
gcc/tree-diagnostic-path.cc
This page took 0.080785 seconds and 6 git commands to generate.