aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c-common.cc49
-rw-r--r--gcc/testsuite/gcc.dg/ubsan/pr120837.c32
2 files changed, 67 insertions, 14 deletions
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 9d69298..362dc50 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -3438,20 +3438,41 @@ pointer_int_sum (location_t loc, enum tree_code resultcode,
an overflow error if the constant is negative but INTOP is not. */
&& (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (intop))
|| (TYPE_PRECISION (TREE_TYPE (intop))
- == TYPE_PRECISION (TREE_TYPE (ptrop)))))
- {
- enum tree_code subcode = resultcode;
- tree int_type = TREE_TYPE (intop);
- if (TREE_CODE (intop) == MINUS_EXPR)
- subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
- /* Convert both subexpression types to the type of intop,
- because weird cases involving pointer arithmetic
- can result in a sum or difference with different type args. */
- ptrop = build_binary_op (EXPR_LOCATION (TREE_OPERAND (intop, 1)),
- subcode, ptrop,
- convert (int_type, TREE_OPERAND (intop, 1)),
- true);
- intop = convert (int_type, TREE_OPERAND (intop, 0));
+ == TYPE_PRECISION (TREE_TYPE (ptrop))))
+ && TYPE_PRECISION (TREE_TYPE (intop)) <= TYPE_PRECISION (sizetype))
+ {
+ tree intop0 = TREE_OPERAND (intop, 0);
+ tree intop1 = TREE_OPERAND (intop, 1);
+ if (TYPE_PRECISION (TREE_TYPE (intop)) != TYPE_PRECISION (sizetype)
+ || TYPE_UNSIGNED (TREE_TYPE (intop)) != TYPE_UNSIGNED (sizetype))
+ {
+ tree optype = c_common_type_for_size (TYPE_PRECISION (sizetype),
+ TYPE_UNSIGNED (sizetype));
+ intop0 = convert (optype, intop0);
+ intop1 = convert (optype, intop1);
+ }
+ tree t = fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (intop0), intop0,
+ convert (TREE_TYPE (intop0), size_exp));
+ intop0 = convert (sizetype, t);
+ if (TREE_OVERFLOW_P (intop0) && !TREE_OVERFLOW (t))
+ intop0 = wide_int_to_tree (TREE_TYPE (intop0), wi::to_wide (intop0));
+ t = fold_build2_loc (loc, MULT_EXPR, TREE_TYPE (intop1), intop1,
+ convert (TREE_TYPE (intop1), size_exp));
+ intop1 = convert (sizetype, t);
+ if (TREE_OVERFLOW_P (intop1) && !TREE_OVERFLOW (t))
+ intop1 = wide_int_to_tree (TREE_TYPE (intop1), wi::to_wide (intop1));
+ intop = build_binary_op (EXPR_LOCATION (intop), TREE_CODE (intop),
+ intop0, intop1, true);
+
+ /* Create the sum or difference. */
+ if (resultcode == MINUS_EXPR)
+ intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
+
+ ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+
+ fold_undefer_and_ignore_overflow_warnings ();
+
+ return ret;
}
/* Convert the integer argument to a type the same size as sizetype
diff --git a/gcc/testsuite/gcc.dg/ubsan/pr120837.c b/gcc/testsuite/gcc.dg/ubsan/pr120837.c
new file mode 100644
index 0000000..97c85c7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/pr120837.c
@@ -0,0 +1,32 @@
+/* PR c/120837 */
+/* { dg-do run } */
+/* { dg-options "-O1 -fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+[[gnu::noipa]] void
+bar (void **x, void **y)
+{
+ x[0] = 0;
+ x[1] = 0;
+ x[2] = 0;
+ y[0] = 0;
+ y[1] = 0;
+ y[2] = 0;
+ y[3] = 0;
+ y[4] = 0;
+}
+
+[[gnu::noipa]] void *
+foo (int x, int y)
+{
+ void *a[3];
+ void *b[5];
+ bar (a, b);
+ return (x > y ? b : a)[y - 1];
+}
+
+int
+main ()
+{
+ if (foo (2, 1) != 0)
+ __builtin_abort ();
+}