analyzer: fix leak false +ve with symbolic writes [PR102208]
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 7 Apr 2022 12:33:26 +0000 (08:33 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Thu, 7 Apr 2022 12:33:26 +0000 (08:33 -0400)
commit88b939b19ab454ab2d932ef292bbc557abe4431c
tree6237a43421f7efe8845780738b59116b0371ce00
parent27bfd13437c773a050f532ed164907de54b5a64f
analyzer: fix leak false +ve with symbolic writes [PR102208]

PR analyzer/102208 reports false positives from -Wanalyzer-malloc-leak.
The root cause is the analyzer getting confused about symbolic writes
that could alias a pointer referencing a malloced buffer.

struct st
{
  void *ptr;
  int arr[10];
};

struct st test (int idx)
{
  struct st s;
  s.ptr = __builtin_malloc (1024);  /* (1) */
  s.arr[idx] = 42;                  /* (2) */
  return s;
}

When removing overlapping bindings at (2),
store::remove_overlapping_bindings was failing to pass on the
uncertainty_t *, and thus when clobbering the binding of s.ptr, the
heap-allocated pointer was not being added to the set of maybe-bound
svalues, and thus being treated as leaking.

This patch fixes this, so that s.ptr from (1) is treated as maybe-bound
after the write at (2), fixing the leak false postive.

Doing so requires the store to be smarter about how clobbering happens
with various combinations of concrete keys and symbolic keys within
concrete clusters and symbolic clusters, so that we don't lose warnings
about definite leaks.

gcc/analyzer/ChangeLog:
PR analyzer/102208
* store.cc (binding_map::remove_overlapping_bindings): Add
"always_overlap" param, using it to generalize to the case where
we want to remove all bindings.  Update "uncertainty" logic to
only record maybe-bound values for cases where there is a symbolic
write involved.
(binding_cluster::mark_region_as_unknown): Split param "reg" into
"reg_to_bind" and "reg_for_overlap".
(binding_cluster::maybe_get_compound_binding): Pass "false" to
binding_map::remove_overlapping_bindings new "always_overlap" param.
(binding_cluster::remove_overlapping_bindings): Determine
"always_overlap" and pass it to
binding_map::remove_overlapping_bindings.
(store::set_value): Pass uncertainty to remove_overlapping_bindings
call.  Update for new param of
binding_cluster::mark_region_as_unknown, passing both the base
region of the iter_cluster, and the lhs_reg.
(store::mark_region_as_unknown): Update for new param of
binding_cluster::mark_region_as_unknown, passing "reg" for both.
(store::remove_overlapping_bindings): Add param "uncertainty", and
pass it on to call to
binding_cluster::remove_overlapping_bindings.
* store.h (binding_map::remove_overlapping_bindings): Add
"always_overlap" param.
(binding_cluster::mark_region_as_unknown): Split param "reg" into
"reg_to_bind" and "reg_for_overlap".
(store::remove_overlapping_bindings): Add param "uncertainty".

gcc/testsuite/ChangeLog:
PR analyzer/102208
* gcc.dg/analyzer/symbolic-9.c: New test.
* gcc.dg/analyzer/torture/leak-pr102308-1.c: New test.
* gcc.dg/analyzer/torture/leak-pr102308-2.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
gcc/analyzer/store.cc
gcc/analyzer/store.h
gcc/testsuite/gcc.dg/analyzer/symbolic-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/torture/leak-pr102308-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/torture/leak-pr102308-2.c [new file with mode: 0644]