On 2024-12-05 05:37, Richard Biener wrote:
VN again is the culprit for exploiting address equivalences before
__builtin_object_size got the chance to do its job.  This time
it isn't about union members but adjacent structure fields where
an address to one after the last element of an array field can
spill over to the next field.

The following protects all out-of-bound accesses on the upper bound
side (singling out TYPE_MAX_VALUE + 1 is more expensive).  It
ignores other out-of-bound addresses that would invoke UB.

Zero-sized arrays are a bit awkward because the C++ represents them
with a -1U upper bound.

There's a similar issue for zero-sized components whose address can
be the same as the adjacent field in C.

Bootstrap and regtest running on x86_64-unknown-linux-gnu.

Does this look OK?

I can't approve of course, but the fix looks correct to me module a comment typo nit below.

Thanks,
Sid

We possbly want to have a zero_size_domain_p () function, or clean up
the zero-size mess - I refrained from dealing with this in this patch.

Richard.

        PR tree-optimization/117912
        * tree-ssa-sccvn.cc (copy_reference_ops_from_ref): For addresses
        of zero-sized components do not set ->off if the object size pass
        didn't run.
        For OOB ARRAY_REF accesses in address expressions avoid setting
        ->off if the object size pass didn't run.
        (valueize_refs_1): Likewise.

        * c-c++-common/torture/pr117912-1.c: New testcase.
        * c-c++-common/torture/pr117912-2.c: Likewise.
        * gcc.dg/torture/pr117912-3.c: Likewise.
---
  .../c-c++-common/torture/pr117912-1.c         | 28 +++++++++++
  .../c-c++-common/torture/pr117912-2.c         | 28 +++++++++++
  gcc/testsuite/gcc.dg/torture/pr117912-3.c     | 18 +++++++
  gcc/tree-ssa-sccvn.cc                         | 47 +++++++++++++++++--
  4 files changed, 118 insertions(+), 3 deletions(-)
  create mode 100644 gcc/testsuite/c-c++-common/torture/pr117912-1.c
  create mode 100644 gcc/testsuite/c-c++-common/torture/pr117912-2.c
  create mode 100644 gcc/testsuite/gcc.dg/torture/pr117912-3.c

diff --git a/gcc/testsuite/c-c++-common/torture/pr117912-1.c 
b/gcc/testsuite/c-c++-common/torture/pr117912-1.c
new file mode 100644
index 00000000000..2750585c7f7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/pr117912-1.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+
+struct S { int a; int b[24]; int c[24]; int d; };
+volatile int *p;
+
+void __attribute__((noipa))
+bar (int *q)
+{
+ p = q;
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+foo (struct S *p)
+{
+  bar (&p->b[24]);
+  bar (&p->c[0]);
+  return __builtin_object_size (&p->c[0], 1);
+}
+
+int
+main()
+{
+  struct S s;
+  __SIZE_TYPE__ x = foo (&s);
+  if (x < sizeof (int) * 24)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/torture/pr117912-2.c 
b/gcc/testsuite/c-c++-common/torture/pr117912-2.c
new file mode 100644
index 00000000000..a3a62157563
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/pr117912-2.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+
+struct S { int a; int b[0]; int c[24]; int d; };
+volatile int *p;
+
+void __attribute__((noipa))
+bar (int *q)
+{
+ p = q;
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+foo (struct S *p)
+{
+  bar (&p->b[0]);
+  bar (&p->c[0]);
+  return __builtin_object_size (&p->c[0], 1);
+}
+
+int
+main()
+{
+  struct S s;
+  __SIZE_TYPE__ x = foo (&s);
+  if (x < sizeof (int) * 24)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr117912-3.c 
b/gcc/testsuite/gcc.dg/torture/pr117912-3.c
new file mode 100644
index 00000000000..b4a036439c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr117912-3.c
@@ -0,0 +1,18 @@
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
+
+long s;
+struct A * __attribute__((noipa)) foo (void) {}
+struct B {};
+struct B __attribute__((noipa)) bar (struct B *) {}
+void baz (void) __attribute__((__error__ ("baz")));
+struct A { int a; struct B b; char c[]; };
+int
+main ()
+{
+  struct A *p = foo ();
+  bar (&p->b);
+  unsigned long q = __builtin_object_size (p->c, 1);
+  if (q < s)
+    baz ();
+}
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index 8d74731a891..9b9fbddb1ab 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -987,9 +987,12 @@ copy_reference_ops_from_ref (tree ref, 
vec<vn_reference_op_s> *result)
                         + (wi::to_offset (bit_offset) >> LOG2_BITS_PER_UNIT));
                    /* Probibit value-numbering zero offset components

Maybe also fix this typo while we're here?

                       of addresses the same before the pass folding
-                      __builtin_object_size had a chance to run.  */
+                      __builtin_object_size had a chance to run.  Likewise
+                      for components of zero size at arbitrary offset.  */
                    if (TREE_CODE (orig) != ADDR_EXPR
-                       || maybe_ne (off, 0)
+                       || (TYPE_SIZE (temp.type)
+                           && !integer_zerop (TYPE_SIZE (temp.type))
+                           && maybe_ne (off, 0))
                        || (cfun->curr_properties & PROP_objsz))
                      off.to_shwi (&temp.off);
                  }
@@ -1010,9 +1013,30 @@ copy_reference_ops_from_ref (tree ref, 
vec<vn_reference_op_s> *result)
            if (! temp.op2)
              temp.op2 = size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (eltype),
                                     size_int (TYPE_ALIGN_UNIT (eltype)));
+           /* Probibit value-numbering addresses of out-of-bound ARRAY_REFs

... and then avoid propagating that typo here :)

+              the same as addresses of other components before the pass
+              folding __builtin_object_size had a chance to run.  */
+           bool avoid_oob = true;
+           if (TREE_CODE (orig) != ADDR_EXPR
+               || cfun->curr_properties & PROP_objsz)
+             avoid_oob = false;
+           else if (poly_int_tree_p (temp.op0))
+             {
+               tree ub = array_ref_up_bound (ref);
+               if (ub
+                   && poly_int_tree_p (ub)
+                   /* ???  The C frontend for T[0] uses [0:] and the
+                      C++ frontend [0:-1U].  See layout_type for how
+                      awkward this is.  */
+                   && !integer_minus_onep (ub)
+                   && known_le (wi::to_poly_offset (temp.op0),
+                                wi::to_poly_offset (ub)))
+                 avoid_oob = false;
+             }
            if (poly_int_tree_p (temp.op0)
                && poly_int_tree_p (temp.op1)
-               && TREE_CODE (temp.op2) == INTEGER_CST)
+               && TREE_CODE (temp.op2) == INTEGER_CST
+               && !avoid_oob)
              {
                poly_offset_int off = ((wi::to_poly_offset (temp.op0)
                                        - wi::to_poly_offset (temp.op1))
@@ -1754,6 +1778,23 @@ re_valueize:
               && poly_int_tree_p (vro->op1)
               && TREE_CODE (vro->op2) == INTEGER_CST)
        {
+         /* Probibit value-numbering addresses of out-of-bound ARRAY_REFs

Likewise.

+            the same as addresses of other components before the pass
+            folding __builtin_object_size had a chance to run.  */
+         if (!(cfun->curr_properties & PROP_objsz)
+             && (*orig)[0].opcode == ADDR_EXPR)
+           {
+             tree dom = TYPE_DOMAIN ((*orig)[i + 1].type);
+             if (!dom
+                 || !TYPE_MAX_VALUE (dom)
+                 || !poly_int_tree_p (TYPE_MAX_VALUE (dom))
+                 || integer_minus_onep (TYPE_MAX_VALUE (dom)))
+               continue;
+             if (!known_le (wi::to_poly_offset (vro->op0),
+                            wi::to_poly_offset (TYPE_MAX_VALUE (dom))))
+               continue;
+           }
+
          poly_offset_int off = ((wi::to_poly_offset (vro->op0)
                                  - wi::to_poly_offset (vro->op1))
                                 * wi::to_offset (vro->op2)

Reply via email to