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.
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. */
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);
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;
}
}
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)
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);
}
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))
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
@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
@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
/* { dg-do run } */
-/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-overflow" } */
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-overflow" } */
#include <limits.h>
/* { 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)
/* { dg-do compile } */
-/* { dg-options "-fsanitize=integer-divide-by-zero" } */
+/* { dg-options "-fsanitize=signed-integer-overflow" } */
void
foo (void)
--- /dev/null
+/* { 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" } */
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* { 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]*" } */
--- /dev/null
+/* { 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'" } */