On Thu, Feb 19, 2026 at 2:50 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 (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]>
> ---
> Changes from v1:
> - Dropped stmt traversal since it's not needed for straight line code.
> - Added a post-dominator check in can_reach_from to shortcut traversal.
>
> Testing:
>
> - No new regressions in x86_64, i686
> - Sqlite test case in BZ #124141 builds without spurious
> -Wdangling-pointers warnings
> - x86_64 bootstrap in progress.
> - This leaves only unresolved false negatives for -Wdangling-pointer.
>
> gcc/gimple-ssa-warn-access.cc | 53 +++++++++++++++++--
> .../c-c++-common/Wdangling-pointer-pr110091.c | 40 ++++++++++++++
> 2 files changed, 89 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..c440c7761f9 100644
> --- a/gcc/gimple-ssa-warn-access.cc
> +++ b/gcc/gimple-ssa-warn-access.cc
> @@ -3854,6 +3854,43 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
> }
> }
>
> +/* Return true if there is a path from FROM to TO. VISITED logs the basic
> + blocks that have been previously visited. */
> +
> +static bool
> +can_reach_from (basic_block from, basic_block to,
> + hash_set<basic_block> &visited)
> +{
> + edge_iterator ei;
> + edge e;
> +
> + /* Avoid walking through the CFG if we can. */
> + if (dominated_by_p (CDI_POST_DOMINATORS, from, to))
> + return true;
> +
> + /* Walk through each successor, focusing on unique, normal, forward
> + edges. */
It's probably better to walk from 'to' to 'from' given there's at most
one function
entry but multiple "ends" of the CFG. The walk should stop at the common
dominator of 'to' and 'from' then (there might not be a common post-dominator),
which might of course be the function entry at worst.
That is, when there is _no_ path from 'from' to 'to' then we want to avoid
walking all paths to exit (or entry if you reverse) to prove that but prune it
at a sensible point.
> + FOR_EACH_EDGE (e, ei, from->succs)
> + {
> + if (e->flags & (EDGE_EH | EDGE_ABNORMAL | EDGE_DFS_BACK))
> + continue;
> +
> + if (visited.add (e->dest))
> + continue;
> +
> + if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
> + continue;
> +
> + if (e->dest == to)
> + return true;
> +
> + if (can_reach_from (e->dest, to, 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
> @@ -3907,10 +3944,18 @@ 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))
> + {
> + hash_set<basic_block> visited;
> + visited.add (inval_bb);
> + if (can_reach_from (inval_bb, use_bb, 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
>