By the time the third instance of warn_access (waccess3) is executed,
its input pointer may have been replaced with a same-address equivalent
to optimize accesses. As a simple fix, avoid warning when it is evident
that the location being accessed may have an alias through its union
sibling.
A future enhancement could be to actually analyze siblings in such cases
to see if there's a match, but it may be more effort than it's worth in
practice.
gcc/ChangeLog:
PR middle-end/123801
* gimple-ssa-warn-access.cc (aliasing_union_addr_p): New
function.
(pass_waccess::maybe_check_access_sizes): Use it.
gcc/testsuite/ChangeLog:
PR middle-end/123801
* gcc.dg/Wstringop-overflow-pr123801.c: New test.
Signed-off-by: Siddhesh Poyarekar <[email protected]>
---
Testing:
- x86_64 bootstrap in progress, no new regressions in non-bootstrap
testing.
- i686 build and test in progress
gcc/gimple-ssa-warn-access.cc | 46 ++++++++++++++++++-
.../gcc.dg/Wstringop-overflow-pr123801.c | 34 ++++++++++++++
2 files changed, 78 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-pr123801.c
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index c8f10cae32f..f8533d48e11 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -3378,6 +3378,45 @@ append_attrname (const std::pair<int, attr_access>
&access,
strcpy (attrstr, TREE_STRING_POINTER (str));
}
+/* In the late (waccess3) pass run, addresses to union members may have been
+ replaced to optimize accesses. This may result in waccess seeing the wrong
+ union member and come to a wrong conclusion about its size. For now, just
+ bail out when we see the possibility of such a situation. This could in
+ future walk through the different types and see if there's a union member at
+ the same address that matches the size of the access. */
+static inline bool
+aliasing_union_addr_p (tree ptr, bool early)
+{
+ if (early)
+ return false;
+
+ /* Thread through the chain of definitions to arrive at an address
+ expression. */
+ while (ptr && TREE_CODE (ptr) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+ if (gimple_code (stmt) == GIMPLE_ASSIGN)
+ ptr = gimple_assign_rhs1 (stmt);
+ else
+ ptr = NULL;
+ }
+
+ if (ptr && TREE_CODE (ptr) == ADDR_EXPR)
+ {
+ ptr = TREE_OPERAND (ptr, 0);
+
+ while (handled_component_p (ptr))
+ {
+ if (TREE_CODE (ptr) == COMPONENT_REF
+ && (TREE_CODE (TREE_TYPE (ptr)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (ptr)) == QUAL_UNION_TYPE))
+ return true;
+ ptr = TREE_OPERAND (ptr, 0);
+ }
+ }
+ return false;
+}
+
/* Iterate over attribute access read-only, read-write, and write-only
arguments and diagnose past-the-end accesses and related problems
in the function call EXP. */
@@ -3419,8 +3458,11 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm,
tree fndecl, tree fntype,
/* The pointer is set to null for the entry corresponding to
the size argument. Skip it. It's handled when the entry
- corresponding to the pointer argument comes up. */
- if (!access.second.ptr)
+ corresponding to the pointer argument comes up. Also bail out in
+ waccess3 when the input pointer could end up pointing to a different
+ member of a union, potentially giving a misleading size. */
+ if (!access.second.ptr
+ || aliasing_union_addr_p (access.second.ptr, m_early_checks_p))
continue;
tree ptrtype = fntype_argno_type (fntype, ptridx);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-pr123801.c
b/gcc/testsuite/gcc.dg/Wstringop-overflow-pr123801.c
new file mode 100644
index 00000000000..2881d2b5ea7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-pr123801.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow" } */
+#define SIZE 8
+
+struct probe_locals *pl;
+
+void strlcpy (char *, const char *, long)
+ __attribute__ ((__access__ (__write_only__, 1, 3)));
+
+struct probe_locals
+{
+ union
+ {
+ struct
+ {
+ char __tmp7[SIZE];
+ int __tmp10;
+ int __tmp11;
+ };
+ struct
+ {
+ char __tmp15[SIZE];
+ char __tmp16[SIZE];
+ };
+ };
+};
+
+void
+probe_func(const char *in)
+{
+ pl->__tmp10 = pl->__tmp11 = 0;
+ char *tmp = pl->__tmp16;
+ strlcpy (tmp, in, SIZE);
+}
--
2.52.0