]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-optimization/117912 - bogus address equivalences for __builtin_object_size
authorRichard Biener <rguenther@suse.de>
Thu, 5 Dec 2024 09:47:13 +0000 (10:47 +0100)
committerRichard Biener <rguenth@gcc.gnu.org>
Fri, 4 Apr 2025 10:09:23 +0000 (12:09 +0200)
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.

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.
* c-c++-common/torture/pr117912-3.c: Likewise.

(cherry picked from commit 233972ab3b5338d7a5d1d7af9108c1f366170e44)

gcc/testsuite/c-c++-common/torture/pr117912-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/pr117912-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/torture/pr117912-3.c [new file with mode: 0644]
gcc/tree-ssa-sccvn.cc

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 (file)
index 0000000..2750585
--- /dev/null
@@ -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 (file)
index 0000000..a3a6215
--- /dev/null
@@ -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/c-c++-common/torture/pr117912-3.c b/gcc/testsuite/c-c++-common/torture/pr117912-3.c
new file mode 100644 (file)
index 0000000..64e981d
--- /dev/null
@@ -0,0 +1,61 @@
+/* { dg-do run } */
+/* { dg-additional-options "-std=gnu++20" { target c++ } } */
+
+struct B {};
+struct A { int a;
+#ifdef __cplusplus
+          [[no_unique_address]]
+#endif
+          struct B b;
+          char c[]; };
+volatile void *p;
+
+void __attribute__((noipa))
+bar (void *q)
+{
+  p = q;
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+foo (struct A *p)
+{
+  bar (&p->b);
+  bar (&p->c);
+  return __builtin_object_size (&p->c, 1);
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+baz (void)
+{
+  struct A *p = (struct A *) __builtin_malloc (__builtin_offsetof (struct A, c) + 64);
+  bar (&p->b);
+  bar (&p->c);
+  return __builtin_object_size (&p->c, 1);
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+qux (struct A *p)
+{
+  bar (&p->b);
+  bar (&p->c);
+  return __builtin_object_size (&p->c, 3);
+}
+
+__SIZE_TYPE__ __attribute__((noipa))
+boo (void)
+{
+  struct A *p = (struct A *) __builtin_malloc (__builtin_offsetof (struct A, c) + 64);
+  bar (&p->b);
+  bar (&p->c);
+  return __builtin_object_size (&p->c, 3);
+}
+
+int
+main ()
+{
+  static struct A a = { .a = 1, .b = {}, .c = { 1, 2, 3, 4, 0 } };
+  if (foo (&a) < 5)
+    __builtin_abort ();
+  if (baz () < 64)
+    __builtin_abort ();
+}
index cda03e0aa685fd02af60ed29ad6ae1c2c9975e89..60a4826e58019938f1e9e0d08391b70786fc46d8 100644 (file)
@@ -943,11 +943,14 @@ copy_reference_ops_from_ref (tree ref, vec<vn_reference_op_s> *result)
                    poly_offset_int off
                      = (wi::to_poly_offset (this_offset)
                         + (wi::to_offset (bit_offset) >> LOG2_BITS_PER_UNIT));
-                   /* Probibit value-numbering zero offset components
+                   /* Prohibit value-numbering zero offset components
                       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_nonzerop (TYPE_SIZE (temp.type))
+                           && maybe_ne (off, 0))
                        || (cfun->curr_properties & PROP_objsz))
                      off.to_shwi (&temp.off);
                  }
@@ -968,9 +971,31 @@ 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)));
+           /* Prohibit value-numbering addresses of one-after-the-last
+              element ARRAY_REFs 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))
@@ -1706,6 +1731,24 @@ re_valueize:
               && poly_int_tree_p (vro->op1)
               && TREE_CODE (vro->op2) == INTEGER_CST)
        {
+           /* Prohibit value-numbering addresses of one-after-the-last
+              element ARRAY_REFs 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)