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

Reply via email to