diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/c-family/c-common.cc | 49 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ubsan/pr120837.c | 32 |
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 (); +} |