On Wed, Feb 18, 2026 at 7:23 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 there is a possible path from
> the invalidating statement block to the use block without any
> intervening blocks that may clobber or assign to the storage in
> question.
>
> This resolves all outstanding false positives with -Wdangling-pointers
> in bugzilla.
>
> gcc/ChangeLog:
>
>         PR middle-end/110091
>         PR middle-end/124141
>         * gimple-ssa-warn-access.cc (can_reach_from): 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]>
> ---
> Testing notes:
> - Tested on x86_64 to verify that there are no new regressions.  Also
>   verified with the preprocessesed sqlite reproducer in pr124141.
> - i686 test run in progress.
> - x86_64 bootstrap in progress.
>
>  gcc/gimple-ssa-warn-access.cc                 | 64 ++++++++++++++++++-
>  .../c-c++-common/Wdangling-pointer-pr110091.c | 40 ++++++++++++
>  2 files changed, 101 insertions(+), 3 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..a7155037d8a 100644
> --- a/gcc/gimple-ssa-warn-access.cc
> +++ b/gcc/gimple-ssa-warn-access.cc
> @@ -3854,6 +3854,53 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
>      }
>  }
>
> +/* Return true if there is a path from FROM to TO where CLOBVAR has not been
> +   written to.  INVAL_BB is the original FROM that was passed in from the
> +   toplevel caller, VISITED logs the basic blocks that have been previously
> +   visited.  */
> +
> +static bool
> +can_reach_from (basic_block inval_bb, basic_block from, basic_block to,
> +               tree clobvar, hash_set<basic_block> &visited)
> +{
> +  edge_iterator ei;
> +  edge e;
> +
> +  /* Skip over inval_bb.  */
> +  if (inval_bb != from)
> +    for (auto gsi = gsi_start_bb (from); !gsi_end_p (gsi);
> +        gsi_next_nondebug (&gsi))
> +      {
> +       gimple *stmt = gsi_stmt (gsi);
> +       if (gimple_code (stmt) == GIMPLE_ASSIGN
> +           && clobvar == gimple_assign_lhs (stmt))

I am not sure this is correct, this only works if clobvar does not escape.
I think you need to use the alias oracle here to query if the stmt
clobbers clobvar or not.

Also I don't like introducing an extra walk of the IR . Especially
since this would be O(SUM(STMT,BB)) .
There must be a way to tell quickly if there is a path from BB1 to BB2
without walking the CFG.

> +         return false;
> +      }
> +
> +  FOR_EACH_EDGE (e, ei, from->succs)
> +    {
> +      if (e->flags & (EDGE_EH | EDGE_ABNORMAL | EDGE_DFS_BACK))
> +       continue;
> +
> +      /* Punt on loops.  */
> +      if (visited.add (e->dest))
> +       continue;
> +
> +      if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
> +       continue;
> +
> +      /* If we reach TO, also check if CLOBVAR is being clobbered or assigned
> +        to anywhere between it and the exit block.  */
> +      if (e->dest == to)
> +       return true;
> +
> +      if (can_reach_from (inval_bb, e->dest, to, clobvar, visited))
> +       return true;
> +    }
> +
> +  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
> @@ -3874,6 +3921,7 @@ pass_waccess::use_after_inval_p (gimple *inval_stmt, 
> gimple *use_stmt,
>
>    if (inval_bb != use_bb)
>      {
> +      /* Look for a quick win with dominator information.  */
>        if (dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb))
>         return true;
>
> @@ -3907,10 +3955,20 @@ 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
> +      /* 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);
> +        point.  If there's a path from INVAL_BB to USE_BB without any
> +        assignments or clobbers to CLOBVAR, then this is an access through a
> +        dangling pointer.  */
> +      if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun))
> +       {
> +         hash_set<basic_block> visited;
> +         visited.add (inval_bb);
> +         if (can_reach_from (inval_bb, inval_bb, use_bb, clobvar, visited))
> +           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