From: Jakub Jelinek Date: Wed, 9 Nov 2016 15:37:28 +0000 (+0100) Subject: flag-types.h (enum sanitize_code): Add SANITIZE_SHIFT_BASE and SANITIZE_SHIFT_EXPONENT... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=2e955d50a928eac736c1e4a6eddc932d3400200a;p=gcc.git flag-types.h (enum sanitize_code): Add SANITIZE_SHIFT_BASE and SANITIZE_SHIFT_EXPONENT... * flag-types.h (enum sanitize_code): Add SANITIZE_SHIFT_BASE and SANITIZE_SHIFT_EXPONENT, change SANITIZE_SHIFT to bitwise or of them, renumber other enumerators. * opts.c (sanitizer_opts): Add shift-base and shift-exponent. * doc/invoke.texi: Document -fsanitize=shift-base and -fsanitize-shift-exponent, document -fsanitize=shift as having those 2 suboptions. c-family/ * c-ubsan.c (ubsan_instrument_shift): Handle split -fsanitize=shift-base and -fsanitize=shift-exponent. testsuite/ * gcc.dg/ubsan/c99-shift-3.c: New test. * gcc.dg/ubsan/c99-shift-4.c: New test. * gcc.dg/ubsan/c99-shift-5.c: New test. * gcc.dg/ubsan/c99-shift-6.c: New test. From-SVN: r242005 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2188dbfe5e3..355148b2c72 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2016-11-09 Jakub Jelinek + + * flag-types.h (enum sanitize_code): Add SANITIZE_SHIFT_BASE + and SANITIZE_SHIFT_EXPONENT, change SANITIZE_SHIFT to bitwise + or of them, renumber other enumerators. + * opts.c (sanitizer_opts): Add shift-base and shift-exponent. + * doc/invoke.texi: Document -fsanitize=shift-base and + -fsanitize-shift-exponent, document -fsanitize=shift as + having those 2 suboptions. + 2016-11-09 Richard Biener * fold-const.c (tree_swap_operands_p): Remove unused arg. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 5207c34c912..89b3043ba48 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2016-11-09 Jakub Jelinek + + * c-ubsan.c (ubsan_instrument_shift): Handle split + -fsanitize=shift-base and -fsanitize=shift-exponent. + 2016-11-07 Jason Merrill * c.opt (Wc++1z-compat): New. diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c index 482cce19f3c..6f93d80a746 100644 --- a/gcc/c-family/c-ubsan.c +++ b/gcc/c-family/c-ubsan.c @@ -130,7 +130,8 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, /* If this is not a signed operation, don't perform overflow checks. Also punt on bit-fields. */ if (TYPE_OVERFLOW_WRAPS (type0) - || GET_MODE_BITSIZE (TYPE_MODE (type0)) != TYPE_PRECISION (type0)) + || GET_MODE_BITSIZE (TYPE_MODE (type0)) != TYPE_PRECISION (type0) + || (flag_sanitize & SANITIZE_SHIFT_BASE) == 0) ; /* For signed x << y, in C99/C11, the following: @@ -171,8 +172,27 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, /* In case we have a SAVE_EXPR in a conditional context, we need to make sure it gets evaluated before the condition. */ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t); - t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, - tt ? tt : integer_zero_node); + + enum sanitize_code recover_kind = SANITIZE_SHIFT_EXPONENT; + tree else_t = void_node; + if (tt) + { + if ((flag_sanitize & SANITIZE_SHIFT_EXPONENT) == 0) + { + t = fold_build1 (TRUTH_NOT_EXPR, boolean_type_node, t); + t = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, t, tt); + recover_kind = SANITIZE_SHIFT_BASE; + } + else + { + if (flag_sanitize_undefined_trap_on_error + || ((!(flag_sanitize_recover & SANITIZE_SHIFT_EXPONENT)) + == (!(flag_sanitize_recover & SANITIZE_SHIFT_BASE)))) + t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt); + else + else_t = tt; + } + } if (flag_sanitize_undefined_trap_on_error) tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); @@ -185,7 +205,7 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, data = build_fold_addr_expr_loc (loc, data); enum built_in_function bcode - = (flag_sanitize_recover & SANITIZE_SHIFT) + = (flag_sanitize_recover & recover_kind) ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; tt = builtin_decl_explicit (bcode); @@ -193,8 +213,22 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, op1 = unshare_expr (op1); tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), ubsan_encode_value (op1)); + if (else_t != void_node) + { + bcode = (flag_sanitize_recover & SANITIZE_SHIFT_BASE) + ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS + : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; + tree else_tt = builtin_decl_explicit (bcode); + op0 = unshare_expr (op0); + op1 = unshare_expr (op1); + else_tt = build_call_expr_loc (loc, else_tt, 3, data, + ubsan_encode_value (op0), + ubsan_encode_value (op1)); + else_t = fold_build3 (COND_EXPR, void_type_node, else_t, + else_tt, void_node); + } } - t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node); + t = fold_build3 (COND_EXPR, void_type_node, t, tt, else_t); return t; } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 17c5c225d0c..76b854081bb 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -10560,6 +10560,21 @@ at runtime. Current suboptions are: This option enables checking that the result of a shift operation is not undefined. Note that what exactly is considered undefined differs slightly between C and C++, as well as between ISO C90 and C99, etc. +This option has two suboptions, @option{-fsanitize=shift-base} and +@option{-fsanitize=shift-exponent}. + +@item -fsanitize=shift-exponent +@opindex fsanitize=shift-exponent +This option enables checking that the second argument of a shift operation +is not negative and is smaller than the precision of the promoted first +argument. + +@item -fsanitize=shift-base +@opindex fsanitize=shift-base +If the second argument of a shift operation is within range, check that the +result of a shift operation is not undefined. Note that what exactly is +considered undefined differs slightly between C and C++, as well as between +ISO C90 and C99, etc. @item -fsanitize=integer-divide-by-zero @opindex fsanitize=integer-divide-by-zero diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 816df6b4ad9..6c5a4cc9346 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -211,24 +211,26 @@ enum sanitize_code { /* LeakSanitizer. */ SANITIZE_LEAK = 1UL << 4, /* UndefinedBehaviorSanitizer. */ - SANITIZE_SHIFT = 1UL << 5, - SANITIZE_DIVIDE = 1UL << 6, - SANITIZE_UNREACHABLE = 1UL << 7, - SANITIZE_VLA = 1UL << 8, - SANITIZE_NULL = 1UL << 9, - SANITIZE_RETURN = 1UL << 10, - SANITIZE_SI_OVERFLOW = 1UL << 11, - SANITIZE_BOOL = 1UL << 12, - SANITIZE_ENUM = 1UL << 13, - SANITIZE_FLOAT_DIVIDE = 1UL << 14, - SANITIZE_FLOAT_CAST = 1UL << 15, - SANITIZE_BOUNDS = 1UL << 16, - SANITIZE_ALIGNMENT = 1UL << 17, - SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18, - SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19, - SANITIZE_OBJECT_SIZE = 1UL << 20, - SANITIZE_VPTR = 1UL << 21, - SANITIZE_BOUNDS_STRICT = 1UL << 22, + SANITIZE_SHIFT_BASE = 1UL << 5, + SANITIZE_SHIFT_EXPONENT = 1UL << 6, + SANITIZE_DIVIDE = 1UL << 7, + SANITIZE_UNREACHABLE = 1UL << 8, + SANITIZE_VLA = 1UL << 9, + SANITIZE_NULL = 1UL << 10, + SANITIZE_RETURN = 1UL << 11, + SANITIZE_SI_OVERFLOW = 1UL << 12, + SANITIZE_BOOL = 1UL << 13, + SANITIZE_ENUM = 1UL << 14, + SANITIZE_FLOAT_DIVIDE = 1UL << 15, + SANITIZE_FLOAT_CAST = 1UL << 16, + SANITIZE_BOUNDS = 1UL << 17, + SANITIZE_ALIGNMENT = 1UL << 18, + SANITIZE_NONNULL_ATTRIBUTE = 1UL << 19, + SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 20, + SANITIZE_OBJECT_SIZE = 1UL << 21, + SANITIZE_VPTR = 1UL << 22, + SANITIZE_BOUNDS_STRICT = 1UL << 23, + SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM diff --git a/gcc/opts.c b/gcc/opts.c index 2f230cea315..d2d6100c0f7 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1477,6 +1477,8 @@ const struct sanitizer_opts_s sanitizer_opts[] = SANITIZER_OPT (thread, SANITIZE_THREAD, false), SANITIZER_OPT (leak, SANITIZE_LEAK, false), SANITIZER_OPT (shift, SANITIZE_SHIFT, true), + SANITIZER_OPT (shift-base, SANITIZE_SHIFT_BASE, true), + SANITIZER_OPT (shift-exponent, SANITIZE_SHIFT_EXPONENT, true), SANITIZER_OPT (integer-divide-by-zero, SANITIZE_DIVIDE, true), SANITIZER_OPT (undefined, SANITIZE_UNDEFINED, true), SANITIZER_OPT (unreachable, SANITIZE_UNREACHABLE, false), diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5fb53383b5d..dd221bbb136 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2016-11-09 Jakub Jelinek + + * gcc.dg/ubsan/c99-shift-3.c: New test. + * gcc.dg/ubsan/c99-shift-4.c: New test. + * gcc.dg/ubsan/c99-shift-5.c: New test. + * gcc.dg/ubsan/c99-shift-6.c: New test. + 2016-11-09 Martin Liska * gcc.dg/tree-ssa/builtins-folding-generic.c (main): Add new diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-3.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-3.c new file mode 100644 index 00000000000..ebb4f660ea9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-3.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift-base -fno-sanitize=shift-exponent -w -std=c99" } */ + +int +main (void) +{ + int a = -42; + int b = -43; + volatile int c = 129; + int d = 1; + a << 1; + b << c; + a << 1; + d <<= 31; +} +/* { dg-output "left shift of negative value -42\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*left shift of negative value -42\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*left shift of 1 by 31 places cannot be represented in type 'int'" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-4.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-4.c new file mode 100644 index 00000000000..95c9616fb57 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-4.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift-exponent -fno-sanitize=shift-base -w -std=c99" } */ + +int +main (void) +{ + int a = -42; + int b = -43; + volatile int c = 129; + int d = 1; + b << c; + a << 1; + a << 1; + d <<= 31; + b << (c + 1); +} +/* { dg-output "shift exponent 129 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 130 is too large for \[^\n\r]*-bit type 'int'" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-5.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-5.c new file mode 100644 index 00000000000..0697997392e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-5.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-shouldfail "ubsan" } */ +/* { dg-options "-fsanitize=shift -fsanitize-recover=shift-base -fno-sanitize-recover=shift-exponent -w -std=c99" } */ + +int +main (void) +{ + int a = -42; + int b = -43; + volatile int c = 129; + int d = 1; + a << 1; + a << 1; + d <<= 31; + b << c; + a << 1; +} +/* { dg-output "left shift of negative value -42\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*left shift of negative value -42\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*left shift of 1 by 31 places cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 129 is too large for \[^\n\r]*-bit type 'int'" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-6.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-6.c new file mode 100644 index 00000000000..8bf700c5a47 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-6.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-shouldfail "ubsan" } */ +/* { dg-options "-fsanitize=shift -fsanitize-recover=shift-exponent -fno-sanitize-recover=shift-base -w -std=c99" } */ + +int +main (void) +{ + int a = -42; + int b = -43; + volatile int c = 129; + 1 << c; + 1 << (c + 1); + a << 1; + b << 1; +} +/* { dg-output "shift exponent 129 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*shift exponent 130 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*left shift of negative value -42\[^\n\r]*(\n|\r\n|\r)" } */