analyzer: stop exploring the path after certain diagnostics [PR108830]
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 21 Feb 2023 21:58:36 +0000 (16:58 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 21 Feb 2023 21:58:36 +0000 (16:58 -0500)
commit8f6369157917a4371b5d66dfe82b84aded3b8268
tree1ea28ef4b1c76fb32d8c968d3d6fd82050be9f0d
parentb2ef02e8cbbaf95fee98be255f697f47193960ec
analyzer: stop exploring the path after certain diagnostics [PR108830]

PR analyzer/108830 reports a situation in which there are lots of
followup -Wanalyzer-null-dereference warnings after the first access of
a NULL pointer, leading to very noisy output from -fanalyzer.

The analyzer's logic for stopping emitting multiple warnings from a
state machine doesn't quite work for NULL pointers: it attempts to
transition the malloc state machine's NULL pointer to the "stop" state,
which doesn't seem to make much sense in retrospect, and seems to get
confused over types.

Similarly, poisoned_value_diagnostic can be very noisy for uninit
variables, emitting a warning for every access to an uninitialized
variable.  In theory, region_model::check_for_poison makes some attempts
to suppress followups, but only for the symbolic value itself; if the
user's code keeps accessing the same region, we would get a warning on
each one.  For example, this showed up in Doom's s_sound.c where there
were 7 followup uninit warnings after the first uninit warning in
"S_ChangeMusic".

This patch adds an extra mechanism, giving pending diagnostics the
option of stopping the analysis of an execution path if they're saved
for emission on it, and turning this on for these warnings:
  -Wanalyzer-null-dereference
  -Wanalyzer-null-argument
  -Wanalyzer-use-after-free
  -Wanalyzer-use-of-pointer-in-stale-stack-frame
  -Wanalyzer-use-of-uninitialized-value

Doing so should hopefully reduce the cascades of diagnostics that
-fanalyzer can sometimes emit.

I added a -fno-analyzer-suppress-followups for the cases where you
really want the followup warnings (e.g. in some DejaGnu tests, and
for microbenchmarks of UB detection, such as PR analyzer/104224).

Integration testing shows this patch reduces the number of probable
false positives reported by 94, and finds one more true positive:

Comparison: 9.34% -> 10.91%
  GOOD:  66 ->  67  (+1)
   BAD: 641 -> 547 (-94)

where the affected warnings/projects are:

  -Wanalyzer-null-dereference: 0.00% GOOD: 0 BAD: 269 -> 239 (-30)
     Unclassified: 257 -> 228 (-29)
                  apr-1.7.0:  12 ->   5  (-7)
                       doom:   1 ->   0  (-1)
              haproxy-2.7.1:  47 ->  41  (-6)
       ImageMagick-7.1.0-57:  13 ->   9  (-4)
                 qemu-7.2.0: 165 -> 154 (-11)

      Known false: 7 -> 6 (-1)
                   xz-5.4.0:   4 ->   3  (-1)

  -Wanalyzer-use-of-uninitialized-value: 0.00% GOOD: 0 BAD: 143 -> 80 (-63)
      Known false: 47 -> 16 (-31)
                       doom: 42 -> 11 (-31)

     Unclassified: 96 -> 64 (-32)
              coreutils-9.1: 14 -> 10  (-4)
              haproxy-2.7.1: 29 -> 23  (-6)
                 qemu-7.2.0: 48 -> 26 (-22)

  -Wanalyzer-null-argument: 0.00% -> 2.33% GOOD: 0 -> 1 (+1) BAD: 43 -> 42 (-1)
     Unclassified: 39 -> 38 (-1)
      due to coreutils-9.1: 9 -> 8 (-1)

    True positive: 0 -> 1 (+1)
      (in haproxy-2.7.1)

gcc/analyzer/ChangeLog:
PR analyzer/108830
* analyzer.opt (fanalyzer-suppress-followups): New option.
* engine.cc (impl_region_model_context::warn): Terminate the path
if the diagnostic's terminate_path_p vfunc returns true and
-fanalyzer-suppress-followups is true (the default).
(impl_sm_context::warn): Likewise, for both overloads.
* pending-diagnostic.h (pending_diagnostic::terminate_path_p): New
vfunc.
* program-state.cc (program_state::on_edge): Terminate the path if
the ctxt requests it during updating the edge.
* region-model.cc (poisoned_value_diagnostic::terminate_path_p):
New vfunc.
* sm-malloc.cc (null_deref::terminate_path_p): New vfunc.
(null_arg::terminate_path_p): New vfunc.

gcc/ChangeLog:
PR analyzer/108830
* doc/invoke.texi: Document -fno-analyzer-suppress-followups.

gcc/testsuite/ChangeLog:
PR analyzer/108830
* gcc.dg/analyzer/attribute-nonnull.c: Update for
-Wanalyzer-use-of-uninitialized-value terminating analysis along
a path.
* gcc.dg/analyzer/call-summaries-2.c: Likewise.
* gcc.dg/analyzer/data-model-1.c: Likewise.
* gcc.dg/analyzer/data-model-5.c: Likewise.
* gcc.dg/analyzer/doom-s_sound-pr108867.c: New test.
* gcc.dg/analyzer/memset-CVE-2017-18549-1.c: Add
-fno-analyzer-suppress-followups.
* gcc.dg/analyzer/null-deref-pr108830.c: New test.
* gcc.dg/analyzer/pipe-1.c: Add -fno-analyzer-suppress-followups.
* gcc.dg/analyzer/pipe-void-return.c: Likewise.
* gcc.dg/analyzer/pipe2-1.c: Likewise.
* gcc.dg/analyzer/pr101547.c: Update for
-Wanalyzer-use-of-uninitialized-value terminating analysis along
a path.
* gcc.dg/analyzer/pr101875.c: Likewise.
* gcc.dg/analyzer/pr104224-split.c: New test, based on...
* gcc.dg/analyzer/pr104224.c: Add
-fno-analyzer-suppress-followups.
* gcc.dg/analyzer/realloc-2.c: Add
-fno-analyzer-suppress-followups.
* gcc.dg/analyzer/realloc-3.c: Likewise.
* gcc.dg/analyzer/realloc-5.c: Likewise.
* gcc.dg/analyzer/stdarg-1-ms_abi.c: Likewise.
* gcc.dg/analyzer/stdarg-1-sysv_abi.c: Likewise.
* gcc.dg/analyzer/stdarg-1.c: Likewise.
* gcc.dg/analyzer/symbolic-1.c: Likewise.
* gcc.dg/analyzer/symbolic-7.c: Update for
-Wanalyzer-use-of-uninitialized-value terminating analysis along a
path.
* gcc.dg/analyzer/uninit-4.c: Likewise.
* gcc.dg/analyzer/uninit-8.c: New test.
* gcc.dg/analyzer/uninit-pr94713.c: Update for
-Wanalyzer-use-of-uninitialized-value terminating analysis along a
path.
* gcc.dg/analyzer/zlib-6a.c: Add -fno-analyzer-suppress-followups.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
33 files changed:
gcc/analyzer/analyzer.opt
gcc/analyzer/engine.cc
gcc/analyzer/pending-diagnostic.h
gcc/analyzer/program-state.cc
gcc/analyzer/region-model.cc
gcc/analyzer/sm-malloc.cc
gcc/doc/invoke.texi
gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
gcc/testsuite/gcc.dg/analyzer/call-summaries-2.c
gcc/testsuite/gcc.dg/analyzer/data-model-1.c
gcc/testsuite/gcc.dg/analyzer/data-model-5.c
gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c
gcc/testsuite/gcc.dg/analyzer/null-deref-pr108830.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/pipe-1.c
gcc/testsuite/gcc.dg/analyzer/pipe-void-return.c
gcc/testsuite/gcc.dg/analyzer/pipe2-1.c
gcc/testsuite/gcc.dg/analyzer/pr101547.c
gcc/testsuite/gcc.dg/analyzer/pr101875.c
gcc/testsuite/gcc.dg/analyzer/pr104224-split.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/pr104224.c
gcc/testsuite/gcc.dg/analyzer/realloc-2.c
gcc/testsuite/gcc.dg/analyzer/realloc-3.c
gcc/testsuite/gcc.dg/analyzer/realloc-5.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
gcc/testsuite/gcc.dg/analyzer/stdarg-1.c
gcc/testsuite/gcc.dg/analyzer/symbolic-1.c
gcc/testsuite/gcc.dg/analyzer/symbolic-7.c
gcc/testsuite/gcc.dg/analyzer/uninit-4.c
gcc/testsuite/gcc.dg/analyzer/uninit-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/uninit-pr94713.c
gcc/testsuite/gcc.dg/analyzer/zlib-6a.c