On Fri, 1 Oct 2021, Jakub Jelinek wrote:

> Hi!
> 
> As noted by Richi, in clang INT_MIN / -1 is instrumented under
> -fsanitize=signed-integer-overflow rather than
> -fsanitize=integer-divide-by-zero as we did and doing it in the former
> makes more sense, as it is overflow during division rather than division
> by zero.
> I've verified on godbolt that clang behaved that way since 3.2-ish times or
> so when sanitizers were added.
> Furthermore, we've been using
> -f{,no-}sanitize-recover=integer-divide-by-zero to decide on the float
> -fsanitize=float-divide-by-zero instrumentation _abort suffix.
> The case where INT_MIN / -1 is instrumented by one sanitizer and
> x / 0 by another one when both are enabled is slightly harder if
> the -f{,no-}sanitize-recover={integer-divide-by-zero,signed-integer-overflow}
> flags differ, then we need to emit both __ubsan_handle_divrem_overflow
> and __ubsan_handle_divrem_overflow_abort calls guarded by their respective
> checks rather than one guarded by check1 || check2.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.  Maybe the change is worth mentioning in changes.html or maybe it's
just a bugfix...

Thanks,
Richard.

> 2021-10-01  Jakub Jelinek  <ja...@redhat.com>
>           Richard Biener  <rguent...@suse.de>
> 
>       PR sanitizer/102515
> gcc/
>       * doc/invoke.texi (-fsanitize=integer-divide-by-zero): Remove
>       INT_MIN / -1 division detection from here ...
>       (-fsanitize=signed-integer-overflow): ... and add it here.
> gcc/c-family/
>       * c-ubsan.c (ubsan_instrument_division): Check the right
>       flag_sanitize_recover bit, depending on which sanitization
>       is done.  Sanitize INT_MIN / -1 under SANITIZE_SI_OVERFLOW
>       rather than SANITIZE_DIVIDE.  If both SANITIZE_SI_OVERFLOW
>       and SANITIZE_DIVIDE is enabled, neither check is known
>       to be false and flag_sanitize_recover bits for those two
>       aren't the same, emit both __ubsan_handle_divrem_overflow
>       and __ubsan_handle_divrem_overflow_abort calls.
> gcc/c/
>       * c-typeck.c (build_binary_op): Call ubsan_instrument_division
>       for division even for SANITIZE_SI_OVERFLOW.
> gcc/cp/
>       * typeck.c (cp_build_binary_op): Call ubsan_instrument_division
>       for division even for SANITIZE_SI_OVERFLOW.
> gcc/testsuite/
>       * c-c++-common/ubsan/div-by-zero-3.c: Use
>       -fsanitize=signed-integer-overflow instead of
>       -fsanitize=integer-divide-by-zero.
>       * c-c++-common/ubsan/div-by-zero-5.c: Likewise.
>       * c-c++-common/ubsan/div-by-zero-4.c: Likewise.  Add
>       -fsanitize-undefined-trap-on-error.
>       * c-c++-common/ubsan/float-div-by-zero-2.c: New test.
>       * c-c++-common/ubsan/overflow-div-1.c: New test.
>       * c-c++-common/ubsan/overflow-div-2.c: New test.
>       * c-c++-common/ubsan/overflow-div-3.c: New test.
> 
> --- gcc/doc/invoke.texi.jj    2021-09-29 10:07:41.841880681 +0200
> +++ gcc/doc/invoke.texi       2021-09-30 20:21:37.736147295 +0200
> @@ -15229,7 +15229,7 @@ ISO C90 and C99, etc.
>  
>  @item -fsanitize=integer-divide-by-zero
>  @opindex fsanitize=integer-divide-by-zero
> -Detect integer division by zero as well as @code{INT_MIN / -1} division.
> +Detect integer division by zero.
>  
>  @item -fsanitize=unreachable
>  @opindex fsanitize=unreachable
> @@ -15261,7 +15261,8 @@ returning a value.  This option works in
>  @opindex fsanitize=signed-integer-overflow
>  This option enables signed integer overflow checking.  We check that
>  the result of @code{+}, @code{*}, and both unary and binary @code{-}
> -does not overflow in the signed arithmetics.  Note, integer promotion
> +does not overflow in the signed arithmetics.  This also detects
> +@code{INT_MIN / -1} signed division.  Note, integer promotion
>  rules must be taken into account.  That is, the following is not an
>  overflow:
>  @smallexample
> --- gcc/c-family/c-ubsan.c.jj 2021-01-04 10:25:50.355103357 +0100
> +++ gcc/c-family/c-ubsan.c    2021-09-30 20:18:53.273433013 +0200
> @@ -39,8 +39,9 @@ along with GCC; see the file COPYING3.
>  tree
>  ubsan_instrument_division (location_t loc, tree op0, tree op1)
>  {
> -  tree t, tt;
> +  tree t, tt, x = NULL_TREE;
>    tree type = TREE_TYPE (op0);
> +  enum sanitize_code flag = SANITIZE_DIVIDE;
>  
>    /* At this point both operands should have the same type,
>       because they are already converted to RESULT_TYPE.
> @@ -58,24 +59,42 @@ ubsan_instrument_division (location_t lo
>                    op1, build_int_cst (type, 0));
>    else if (TREE_CODE (type) == REAL_TYPE
>          && sanitize_flags_p (SANITIZE_FLOAT_DIVIDE))
> -    t = fold_build2 (EQ_EXPR, boolean_type_node,
> -                  op1, build_real (type, dconst0));
> +    {
> +      t = fold_build2 (EQ_EXPR, boolean_type_node,
> +                    op1, build_real (type, dconst0));
> +      flag = SANITIZE_FLOAT_DIVIDE;
> +    }
>    else
> -    return NULL_TREE;
> +    t = NULL_TREE;
>  
>    /* We check INT_MIN / -1 only for signed types.  */
>    if (TREE_CODE (type) == INTEGER_TYPE
> -      && sanitize_flags_p (SANITIZE_DIVIDE)
> +      && sanitize_flags_p (SANITIZE_SI_OVERFLOW)
>        && !TYPE_UNSIGNED (type))
>      {
> -      tree x;
>        tt = fold_build2 (EQ_EXPR, boolean_type_node, unshare_expr (op1),
>                       build_int_cst (type, -1));
>        x = fold_build2 (EQ_EXPR, boolean_type_node, op0,
>                      TYPE_MIN_VALUE (type));
>        x = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, x, tt);
> -      t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, x);
> +      if (t == NULL_TREE || integer_zerop (t))
> +     {
> +       t = x;
> +       x = NULL_TREE;
> +       flag = SANITIZE_SI_OVERFLOW;
> +     }
> +      else if (flag_sanitize_undefined_trap_on_error
> +            || (((flag_sanitize_recover & SANITIZE_DIVIDE) == 0)
> +                == ((flag_sanitize_recover & SANITIZE_SI_OVERFLOW) == 0)))
> +     {
> +       t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, x);
> +       x = NULL_TREE;
> +     }
> +      else if (integer_zerop (x))
> +     x = NULL_TREE;
>      }
> +  else if (t == NULL_TREE)
> +    return NULL_TREE;
>  
>    /* If the condition was folded to 0, no need to instrument
>       this expression.  */
> @@ -95,7 +114,7 @@ ubsan_instrument_division (location_t lo
>                                    NULL_TREE);
>        data = build_fold_addr_expr_loc (loc, data);
>        enum built_in_function bcode
> -     = (flag_sanitize_recover & SANITIZE_DIVIDE)
> +     = (flag_sanitize_recover & flag)
>         ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW
>         : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT;
>        tt = builtin_decl_explicit (bcode);
> @@ -103,8 +122,20 @@ ubsan_instrument_division (location_t lo
>        op1 = unshare_expr (op1);
>        tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0),
>                               ubsan_encode_value (op1));
> +      if (x)
> +     {
> +       bcode = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
> +               ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW
> +               : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT;
> +       tree xt = builtin_decl_explicit (bcode);
> +       op0 = unshare_expr (op0);
> +       op1 = unshare_expr (op1);
> +       xt = build_call_expr_loc (loc, xt, 3, data, ubsan_encode_value (op0),
> +                                 ubsan_encode_value (op1));
> +       x = fold_build3 (COND_EXPR, void_type_node, x, xt, void_node);
> +     }
>      }
> -  t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node);
> +  t = fold_build3 (COND_EXPR, void_type_node, t, tt, x ? x : void_node);
>  
>    return t;
>  }
> --- gcc/c/c-typeck.c.jj       2021-09-29 10:07:41.789881404 +0200
> +++ gcc/c/c-typeck.c  2021-09-30 20:50:00.990465683 +0200
> @@ -12733,7 +12733,9 @@ build_binary_op (location_t location, en
>      }
>  
>    if (sanitize_flags_p ((SANITIZE_SHIFT
> -                      | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
> +                      | SANITIZE_DIVIDE
> +                      | SANITIZE_FLOAT_DIVIDE
> +                      | SANITIZE_SI_OVERFLOW))
>        && current_function_decl != NULL_TREE
>        && (doing_div_or_mod || doing_shift)
>        && !require_constant_value)
> @@ -12744,7 +12746,8 @@ build_binary_op (location_t location, en
>        op0 = c_fully_fold (op0, false, NULL);
>        op1 = c_fully_fold (op1, false, NULL);
>        if (doing_div_or_mod && (sanitize_flags_p ((SANITIZE_DIVIDE
> -                                               | SANITIZE_FLOAT_DIVIDE))))
> +                                               | SANITIZE_FLOAT_DIVIDE
> +                                               | SANITIZE_SI_OVERFLOW))))
>       instrument_expr = ubsan_instrument_division (location, op0, op1);
>        else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT))
>       instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
> --- gcc/cp/typeck.c.jj        2021-09-09 10:40:26.096175666 +0200
> +++ gcc/cp/typeck.c   2021-09-30 20:50:23.806147945 +0200
> @@ -6038,7 +6038,9 @@ cp_build_binary_op (const op_location_t
>      }
>  
>    if (sanitize_flags_p ((SANITIZE_SHIFT
> -                      | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
> +                      | SANITIZE_DIVIDE
> +                      | SANITIZE_FLOAT_DIVIDE
> +                      | SANITIZE_SI_OVERFLOW))
>        && current_function_decl != NULL_TREE
>        && !processing_template_decl
>        && (doing_div_or_mod || doing_shift))
> @@ -6050,7 +6052,9 @@ cp_build_binary_op (const op_location_t
>        op1 = fold_non_dependent_expr (op1, complain);
>        tree instrument_expr1 = NULL_TREE;
>        if (doing_div_or_mod
> -       && sanitize_flags_p (SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
> +       && sanitize_flags_p (SANITIZE_DIVIDE
> +                            | SANITIZE_FLOAT_DIVIDE
> +                            | SANITIZE_SI_OVERFLOW))
>       {
>         /* For diagnostics we want to use the promoted types without
>            shorten_binary_op.  So convert the arguments to the
> --- gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c.jj       2020-01-12 
> 11:54:37.029404115 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c  2021-09-30 
> 20:51:00.522636616 +0200
> @@ -1,5 +1,5 @@
>  /* { dg-do run } */
> -/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-overflow" } */
> +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-overflow" } */
>  
>  #include <limits.h>
>  
> --- gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c.jj       2020-01-12 
> 11:54:37.029404115 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c  2021-09-30 
> 20:24:17.051933114 +0200
> @@ -1,5 +1,5 @@
>  /* { dg-do run } */
> -/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-overflow" } */
> +/* { dg-options "-fsanitize=signed-integer-overflow 
> -fsanitize-undefined-trap-on-error -Wno-overflow" } */
>  
>  #define INT_MIN (-__INT_MAX__ - 1)
>  
> --- gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c.jj       2020-01-12 
> 11:54:37.029404115 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-5.c  2021-09-30 
> 20:24:55.559397933 +0200
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-fsanitize=integer-divide-by-zero" } */
> +/* { dg-options "-fsanitize=signed-integer-overflow" } */
>  
>  void
>  foo (void)
> --- gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c.jj 2021-10-01 
> 12:28:23.485086791 +0200
> +++ gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c    2021-10-01 
> 12:29:35.875062499 +0200
> @@ -0,0 +1,18 @@
> +/* { dg-do run } */
> +/* { dg-shouldfail "ubsan" } */
> +/* { dg-options "-fsanitize=float-divide-by-zero 
> -fno-sanitize-recover=float-divide-by-zero 
> -fsanitize-recover=integer-divide-by-zero" } */
> +
> +int
> +main (void)
> +{
> +  volatile float a = 1.3f;
> +  volatile double b = 0.0;
> +  volatile int c = 4;
> +  volatile float res;
> +
> +  res = a / b;
> +
> +  return 0;
> +}
> +
> +/* { dg-output "division by zero" } */
> --- gcc/testsuite/c-c++-common/ubsan/overflow-div-1.c.jj      2021-10-01 
> 12:30:48.269038146 +0200
> +++ gcc/testsuite/c-c++-common/ubsan/overflow-div-1.c 2021-10-01 
> 12:31:49.532171295 +0200
> @@ -0,0 +1,17 @@
> +/* { dg-do run } */
> +/* { dg-options "-fsanitize=integer-divide-by-zero 
> -fno-sanitize-recover=undefined,float-divide-by-zero -Wno-overflow" } */
> +
> +#include <limits.h>
> +
> +int
> +main (void)
> +{
> +  volatile int min = INT_MIN;
> +  volatile int zero = 0;
> +
> +  INT_MIN / -1;
> +  min / -1;
> +  min / (10 * zero - (2 - 1));
> +
> +  return 0;
> +}
> --- gcc/testsuite/c-c++-common/ubsan/overflow-div-2.c.jj      2021-10-01 
> 12:34:28.814917433 +0200
> +++ gcc/testsuite/c-c++-common/ubsan/overflow-div-2.c 2021-10-01 
> 12:50:24.398395074 +0200
> @@ -0,0 +1,41 @@
> +/* { dg-do run { target *-*-linux* *-*-gnu* } } */
> +/* { dg-shouldfail "ubsan" } */
> +/* { dg-options "-fsanitize=undefined 
> -fno-sanitize-recover=integer-divide-by-zero" } */
> +
> +#include <limits.h>
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +int cnt;
> +
> +__attribute__((noipa)) int
> +foo (int x, int y)
> +{
> +  return x / y;
> +}
> +
> +void
> +handler (int i)
> +{
> +  if (cnt++ != 0)
> +    exit (0);
> +  volatile int b = foo (5, 0);
> +  exit (0);
> +}
> +
> +int
> +main (void)
> +{
> +  struct sigaction s;
> +  sigemptyset (&s.sa_mask);
> +  s.sa_handler = handler;
> +  s.sa_flags = 0;
> +  sigaction (SIGFPE, &s, NULL);
> +  volatile int a = foo (INT_MIN, -1);
> +  cnt++;
> +  volatile int b = foo (5, 0);
> +  return 0;
> +}
> +
> +/* { dg-output "division of -2147483648 by -1 cannot be represented in type 
> 'int'\[^\n\r]*(\n|\r\n|\r)" } */
> +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*" } */
> --- gcc/testsuite/c-c++-common/ubsan/overflow-div-3.c.jj      2021-10-01 
> 12:39:28.230680432 +0200
> +++ gcc/testsuite/c-c++-common/ubsan/overflow-div-3.c 2021-10-01 
> 12:50:58.652910346 +0200
> @@ -0,0 +1,41 @@
> +/* { dg-do run { target *-*-linux* *-*-gnu* } } */
> +/* { dg-shouldfail "ubsan" } */
> +/* { dg-options "-fsanitize=undefined 
> -fno-sanitize-recover=signed-integer-overflow" } */
> +
> +#include <limits.h>
> +#include <signal.h>
> +#include <stdlib.h>
> +
> +int cnt;
> +
> +__attribute__((noipa)) int
> +foo (int x, int y)
> +{
> +  return x / y;
> +}
> +
> +void
> +handler (int i)
> +{
> +  if (cnt++ != 0)
> +    exit (0);
> +  volatile int b = foo (INT_MIN, -1);
> +  exit (0);
> +}
> +
> +int
> +main (void)
> +{
> +  struct sigaction s;
> +  sigemptyset (&s.sa_mask);
> +  s.sa_handler = handler;
> +  s.sa_flags = 0;
> +  sigaction (SIGFPE, &s, NULL);
> +  volatile int a = foo (42, 0);
> +  cnt++;
> +  volatile int b = foo (INT_MIN, -1);
> +  return 0;
> +}
> +
> +/* { dg-output "division by zero\[^\n\r]*(\n|\r\n|\r)" } */
> +/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented 
> in type 'int'" } */
> 
>       Jakub
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)

Reply via email to