On Thu, Feb 19, 2026 at 10:51 PM Siddhesh Poyarekar <[email protected]> wrote:
>
> When checking for use of a dangling pointer, use_after_inval_p only
> checks to see if there's a direct path between the use and the exit
> block and that there's a clobber in the way.  This is insufficient to
> prove dangling pointer access since the basic premise of the use being
> reachable from End Of Scope clobber is not tested.
>
> If there's a straight, potentially failing path from use to the exit
> block, add another test to make sure that the use block is actually
> reachable from the invalidating block before warning.
>
> gcc/ChangeLog:
>
>         PR middle-end/110091
>         PR middle-end/124141
>         * gimple-ssa-warn-access.cc (reachable_path_p): New function.
>         (pass_waccess::use_after_inval_p): Use it.
>
> gcc/testsuite/ChangeLog:
>
>         PR middle-end/110091
>         PR middle-end/124141
>         * c-c++-common/Wdangling-pointer-pr110091.c: New test.
>
> Signed-off-by: Siddhesh Poyarekar <[email protected]>
> ---
> Changes from v2:
> - Walk backwards from use_bb and use nearest common dominator as the end
>   point.
> - Clean up and tighten the code a bit.
> - Use a stack based worklist for DFS instead of recursion.
>
> Testing:
> - x86_64 bootstrap in progress
> - Tested x86_64, i686 and the sqlite reproducer
>
>  gcc/gimple-ssa-warn-access.cc                 | 52 +++++++++++++++++--
>  .../c-c++-common/Wdangling-pointer-pr110091.c | 40 ++++++++++++++
>  2 files changed, 88 insertions(+), 4 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/Wdangling-pointer-pr110091.c
>
> diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
> index c8f10cae32f..64c4b99d80d 100644
> --- a/gcc/gimple-ssa-warn-access.cc
> +++ b/gcc/gimple-ssa-warn-access.cc
> @@ -3854,6 +3854,46 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
>      }
>  }
>
> +/* Return true if there is a path from FROM to TO.  */
> +
> +static bool
> +reachable_path_p (basic_block from, basic_block to)
> +{
> +  edge_iterator ei;
> +  edge e;
> +  auto_vec<basic_block> worklist;
> +  hash_set<basic_block> visited;
> +
> +  if (dominated_by_p (CDI_POST_DOMINATORS, from, to))
> +    return true;
> +
> +  /* Stop at the common dominator for both BBs.  */
> +  visited.add (nearest_common_dominator (CDI_DOMINATORS, from, to));
> +
> +  /* Staring at TO, walk backwards through each predecessor, focusing on
> +     unique, normal, forward edges.  */
> +  worklist.safe_push (to);
> +  while (worklist.length () > 0)
> +    {
> +      worklist.pop ();
> +      FOR_EACH_EDGE (e, ei, to->preds)

Errr, you are walking the same BB all the time?  You want

   to = worklist.pop ()?

> +       {
> +         if (e->flags & (EDGE_EH | EDGE_ABNORMAL | EDGE_DFS_BACK))
> +           continue;
> +
> +         if (visited.add (e->src))
> +           continue;
> +
> +         if (dominated_by_p (CDI_POST_DOMINATORS, from, e->src))
> +           return true;

I think this is a) wrong (a single-pred to would immediately claim there's
a path from from to to), you want dominated_by_p  (CDI_DOMIANTORS,
e->src, from),
or rather e->src == from || dominated_by_p ...

note for from == to this function would the return false, so this
check belongs right after
the to = worklist .pop() and check 'to'.

This all means you need some "positive" check for this, if this passed
testing or test-coverage
for the requirement of reachable_path is very weak.   In particluar ...

> +
> +         worklist.safe_push (e->src);
> +       }
> +    }
> +
> +  return false;
> +}
> +
>  /* Return true if either USE_STMT's basic block (that of a pointer's use)
>     is dominated by INVAL_STMT's (that of a pointer's invalidating statement,
>     which is either a clobber or a deallocation call), or if they're in
> @@ -3907,10 +3947,14 @@ pass_waccess::use_after_inval_p (gimple *inval_stmt, 
> gimple *use_stmt,
>           gsi = gsi_start_bb (bb);
>         }
>
> -      /* The use is one of a dangling pointer if a clobber of the variable
> -        [the pointer points to] has not been found before the function exit
> -        point.  */
> -      return bb == EXIT_BLOCK_PTR_FOR_FN (cfun);
> +      /* Clobber of the variable [the pointer points to] has not been found
> +        before the function exit point.  If there's a path from INVAL_BB to
> +        USE_BB, then this is an access through a dangling pointer.  */
> +      if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
> +         && reachable_path_p (inval_bb, use_bb))

... I wonder if your analysis of the original issue is correct.  I've
sofar only looked at
your reachable_path_p implementation.

> +       return true;
> +
> +      return false;
>      }
>
>    if (bitmap_set_bit (m_bb_uids_set, inval_bb->index))
> diff --git a/gcc/testsuite/c-c++-common/Wdangling-pointer-pr110091.c 
> b/gcc/testsuite/c-c++-common/Wdangling-pointer-pr110091.c
> new file mode 100644
> index 00000000000..c26077c54ad
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wdangling-pointer-pr110091.c
> @@ -0,0 +1,40 @@
> +/* PR middle-end/pr110091, middle-end/pr124141: bogus -Wdangling-pointer
> +   warning on known-unreachable code.
> +   { dg-do compile }
> +   { dg-options "-O2 -Wdangling-pointer" } */
> +
> +struct tEntry
> +{
> +    int value;
> +};
> +
> +struct tOut
> +{
> +    int outvalue;
> +};
> +extern struct tOut *out;
> +
> +extern int otherfunc(struct tEntry *);
> +extern void anotherfunc(int val);
> +
> +void bar()
> +{
> +    struct tEntry entry = { 0 };
> +
> +    if (otherfunc(&entry) != 0)
> +    {
> +        return;
> +    }
> +
> +    if (out)
> +    {
> +        out->outvalue = entry.value;
> +    }
> +
> +    anotherfunc(5);
> +}
> +
> +void foo()
> +{
> +    bar();
> +}
> --
> 2.52.0
>

Reply via email to