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
>