Hi!

The following testcase is diagnosed by UBSan as invalid, even when it is
valid.
We have a derived type Base2 at offset 1 with alignment 1 and do:
(const Derived &) ((const Base2 *) this + -1)
but the folder before ubsan in the FE gets a chance to instrument it
optimizes that into:
(const Derived &) this + -1
and so we require that this has 8-byte alignment which Derived class needs.

Fixed by avoiding such an optimization when -fsanitize=alignment is in
effect if it would affect the alignments (and guarded with !in_gimple_form
because we don't really care during GIMPLE, though pointer conversions are
useless then and so such folding isn't needed very much during GIMPLE).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2020-12-30  Jakub Jelinek  <ja...@redhat.com>

        PR c++/98206
        * fold-const.c: Include asan.h.
        (fold_unary_loc): Don't optimize (ptr_type) (((ptr_type2) x) p+ y)
        into ((ptr_type) x) p+ y if sanitizing alignment in GENERIC and
        ptr_type points to type with higher alignment than ptr_type2.

        * g++.dg/ubsan/align-4.C: New test.

--- gcc/fold-const.c.jj 2020-12-21 10:14:39.409173804 +0100
+++ gcc/fold-const.c    2020-12-29 15:40:34.283148245 +0100
@@ -82,6 +82,7 @@ along with GCC; see the file COPYING3.
 #include "attribs.h"
 #include "tree-vector-builder.h"
 #include "vec-perm-indices.h"
+#include "asan.h"
 
 /* Nonzero if we are folding constants inside an initializer; zero
    otherwise.  */
@@ -9392,8 +9393,17 @@ fold_unary_loc (location_t loc, enum tre
          tree arg00 = TREE_OPERAND (arg0, 0);
          tree arg01 = TREE_OPERAND (arg0, 1);
 
-         return fold_build_pointer_plus_loc
-                  (loc, fold_convert_loc (loc, type, arg00), arg01);
+         /* If -fsanitize=alignment, avoid this optimization in GENERIC
+            when the pointed type needs higher alignment than
+            the p+ first operand's pointed type.  */
+         if (!in_gimple_form
+             && sanitize_flags_p (SANITIZE_ALIGNMENT)
+             && (min_align_of_type (TREE_TYPE (type))
+                 > min_align_of_type (TREE_TYPE (TREE_TYPE (arg00)))))
+           return NULL_TREE;
+
+         arg00 = fold_convert_loc (loc, type, arg00);
+         return fold_build_pointer_plus_loc (loc, arg00, arg01);
        }
 
       /* Convert (T1)(~(T2)X) into ~(T1)X if T1 and T2 are integral types
--- gcc/testsuite/g++.dg/ubsan/align-4.C.jj     2020-12-29 15:28:11.811501092 
+0100
+++ gcc/testsuite/g++.dg/ubsan/align-4.C        2020-12-29 15:29:54.198348346 
+0100
@@ -0,0 +1,31 @@
+// PR c++/98206
+// { dg-do run }
+// { dg-options "-fsanitize=alignment -std=c++11 
-fno-sanitize-recover=alignment" }
+
+template <typename Derived>
+struct Base1
+{
+  char c1;
+};
+
+template <typename Derived>
+struct Base2
+{
+  char c2;
+  const Derived &get2 () const { return static_cast<const Derived &> (*this); }
+};
+
+struct X : public Base1<X>, public Base2<X>
+{
+  X (const char *d) : data{d} {}
+  const char *data;
+};
+
+int
+main ()
+{
+  X x = X{"cheesecake"};
+  const char *p = x.get2 ().data;
+  if (p[0] != 'c')
+    __builtin_abort ();
+}

        Jakub

Reply via email to