[PATCH] Add -fsanitize=shift{,-base,-exponent} support

Richard Biener rguenther@suse.de
Tue Nov 8 07:59:00 GMT 2016


On Tue, 8 Nov 2016, Jakub Jelinek wrote:

> Hi!
> 
> In the libsanitizer merge Maxim posted I've noticed that llvm split
> the -fsanitize=shift option into suboption (at least one really weirdly
> named, I have never seen the shift count being called exponent), this
> patch implements the same with the same option names for gcc.
> The interesting part is if the two suboptions disagree on
> -fsanitize-recover, then we need to emit two checks and two calls (one with
> _abort, one without).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok.

Richard.

> 2016-11-08  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* 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.
> 
> --- gcc/flag-types.h.jj	2016-11-07 14:04:29.129755670 +0100
> +++ gcc/flag-types.h	2016-11-07 18:50:54.989652650 +0100
> @@ -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
> --- gcc/opts.c.jj	2016-11-07 14:04:29.131755645 +0100
> +++ gcc/opts.c	2016-11-07 18:50:54.990652637 +0100
> @@ -1477,6 +1477,8 @@ const struct sanitizer_opts_s sanitizer_
>    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),
> --- gcc/doc/invoke.texi.jj	2016-11-07 14:04:28.853759109 +0100
> +++ gcc/doc/invoke.texi	2016-11-07 18:50:54.998652536 +0100
> @@ -10555,6 +10555,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
> --- gcc/c-family/c-ubsan.c.jj	2016-11-07 14:04:29.102756006 +0100
> +++ gcc/c-family/c-ubsan.c	2016-11-07 18:50:54.991652625 +0100
> @@ -130,7 +130,8 @@ ubsan_instrument_shift (location_t loc,
>    /* 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,
>    /* 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,
>        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,
>        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;
>  }
> --- gcc/testsuite/gcc.dg/ubsan/c99-shift-3.c.jj	2016-11-07 18:54:33.916880052 +0100
> +++ gcc/testsuite/gcc.dg/ubsan/c99-shift-3.c	2016-11-07 19:08:08.318566032 +0100
> @@ -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'" } */
> --- gcc/testsuite/gcc.dg/ubsan/c99-shift-4.c.jj	2016-11-07 18:55:20.786286472 +0100
> +++ gcc/testsuite/gcc.dg/ubsan/c99-shift-4.c	2016-11-07 19:16:59.049847278 +0100
> @@ -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'" } */
> --- gcc/testsuite/gcc.dg/ubsan/c99-shift-5.c.jj	2016-11-07 19:11:29.554018440 +0100
> +++ gcc/testsuite/gcc.dg/ubsan/c99-shift-5.c	2016-11-07 19:16:11.676446989 +0100
> @@ -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'" } */
> --- gcc/testsuite/gcc.dg/ubsan/c99-shift-6.c.jj	2016-11-07 19:14:21.190845651 +0100
> +++ gcc/testsuite/gcc.dg/ubsan/c99-shift-6.c	2016-11-07 19:15:53.807673194 +0100
> @@ -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)" } */
> 
> 
> 	Jakub
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)



More information about the Gcc-patches mailing list