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
>