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)
+ {
+ 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;
+
+ 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))
+ 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