Hi!

The following testcase is miscompiled since the introduction of UBSan,
cp_build_array_ref COND_EXPR handling replaces
(cond ? a : b)[idx] with cond ? a[idx] : b[idx], but if there are
SAVE_EXPRs inside of idx, they will be evaluated just in one of the
branches and the other uses uninitialized temporaries.

Fixed by using cp_save_expr and if needed, evaluating it before the
condition; for constant indexes this shouldn't change anything, and for
larger expressions in idx I think the patch should result in smaller
generated code, no need to duplicate all the evaluation in each of the
branches.

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

2025-06-27  Jakub Jelinek  <ja...@redhat.com>

        PR c++/120471
        * typeck.cc (cp_build_array_ref) <case COND_EXPR>: Use cp_save_expr
        on idx.  If it has TREE_SIDE_EFFECTS, add it as lhs of COMPOUND_EXPR
        in first COND_EXPR argument.  Formatting fixes.

        * g++.dg/ubsan/pr120471.C: New test.

--- gcc/cp/typeck.cc.jj 2025-06-17 13:19:03.040958319 +0200
+++ gcc/cp/typeck.cc    2025-06-27 14:25:54.657852009 +0200
@@ -4001,13 +4001,14 @@ cp_build_array_ref (location_t loc, tree
       }
 
     case COND_EXPR:
-      ret = build_conditional_expr
-              (loc, TREE_OPERAND (array, 0),
-              cp_build_array_ref (loc, TREE_OPERAND (array, 1), idx,
-                                  complain),
-              cp_build_array_ref (loc, TREE_OPERAND (array, 2), idx,
-                                  complain),
-              complain);
+      tree op0, op1, op2;
+      idx = cp_save_expr (idx);
+      op0 = TREE_OPERAND (array, 0);
+      if (TREE_SIDE_EFFECTS (idx))
+       op0 = build_compound_expr (loc, idx, op0);
+      op1 = cp_build_array_ref (loc, TREE_OPERAND (array, 1), idx, complain);
+      op2 = cp_build_array_ref (loc, TREE_OPERAND (array, 2), idx, complain);
+      ret = build_conditional_expr (loc, op0, op1, op2, complain);
       protected_set_expr_location (ret, loc);
       return ret;
 
--- gcc/testsuite/g++.dg/ubsan/pr120471.C.jj    2025-06-27 14:31:31.340547836 
+0200
+++ gcc/testsuite/g++.dg/ubsan/pr120471.C       2025-06-27 14:31:12.347790637 
+0200
@@ -0,0 +1,21 @@
+// PR c++/120471
+// { dg-do run }
+// { dg-options "-fsanitize=undefined" }
+
+volatile int b[1], a[1];
+
+void
+foo (int x)
+{
+  volatile int c = 21;
+  volatile int v = (x % 2 ? b : a)[c % 3];
+  if (v != 0)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo (1);
+  foo (2);
+}

        Jakub

Reply via email to