ISan in FE.
Handle all s and u cases.
Add unit and regression tests.
Change-Id: I3f60f751598d904683264372541c2d3be0076768
const_call_expr_arg_iterator iter;
if (TREE_CODE (t) != CALL_EXPR
+ || !CALL_EXPR_FN (t)
|| TREE_CODE (CALL_EXPR_FN (t)) != ADDR_EXPR)
return END_BUILTINS;
tree ptrop, tree intop, bool complain)
{
tree size_exp, ret;
+ HOST_WIDE_INT op1 = 0;
/* The result is a pointer of the same type that is being added. */
tree result_type = TREE_TYPE (ptrop);
+ if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+ {
+ /* First, try to avoid FPs if INTOP is negative constant. */
+ if (tree_fits_shwi_p (intop))
+ op1 = tree_to_shwi (intop);
+ }
+
if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)
{
if (complain && warn_pointer_arith)
intop = wide_int_to_tree (TREE_TYPE (intop), intop);
}
- /* Create the sum or difference. */
- if (resultcode == MINUS_EXPR)
- intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
+ if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+ {
+ tree tmp = NULL_TREE;
+ /* First, try to avoid FPs if INTOP is negative constant. */
+ if (op1 != 0)
+ {
+ if (op1 < 0)
+ {
+ resultcode = (resultcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
+ op1 = -op1;
+ }
+ tmp = build_int_cst (sizetype, op1);
+ intop = build_binary_op (loc, MULT_EXPR, tmp,
+ convert (TREE_TYPE (intop), size_exp), 1);
+ }
+ tmp = build_binary_op (loc, resultcode,
+ convert (sizetype, ptrop),
+ convert (long_integer_type_node, intop), 1);
+ ret = convert (result_type, tmp);
+ }
+ else
+ {
+ /* 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);
+ ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+ }
fold_undefer_and_ignore_overflow_warnings ();
if (TREE_CODE (param) == CALL_EXPR)
{
- tree type = TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (param)));
+ tree type
+ = CALL_EXPR_FN (param) ? TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (param)))
+ : TREE_TYPE (param);
tree attrs;
bool found_format_arg = false;
if (op)
CALL_EXPR_ARG (stmt, 0) = op;
}
+
+/* Perform the unsigned and signed integer instrumentation for binary
+ operation CODE. LOC is the source location, RESULT_TYPE is computed type
+ for result of expression, OP0 and OP1 are corresponding operands.
+ UNS0_P and UNS1_P are bool flags indicating whether original OP0 and OP1
+ were signed or unsigned. */
+
+tree
+isan_maybe_instrument_ui (location_t loc, enum tree_code code,
+ tree result_type, tree op0, tree op1,
+ int uns0_p, int uns1_p)
+{
+ tree op0type = TREE_TYPE (op0);
+ tree op1type = TREE_TYPE (op1);
+
+ tree op0_uns_p = build_int_cst (integer_type_node, uns0_p);
+ tree op1_uns_p = build_int_cst (integer_type_node, uns1_p);
+ tree res_uns_p = build_int_cst (integer_type_node,
+ TYPE_UNSIGNED (result_type));
+
+ /* TODO: instrument even two constants. We don't do it right now to avoid
+ nasty errors in array bounds computations such as A[1 + 2 * sizeof (int)].
+ */
+ if ((TREE_CODE (op0) == INTEGER_CST) && (TREE_CODE (op1) == INTEGER_CST))
+ return NULL_TREE;
+
+ /* TODO: instrument for different precision modes. */
+ if ((TYPE_PRECISION (op0type) != TYPE_PRECISION (result_type))
+ || (TYPE_PRECISION (op1type) != TYPE_PRECISION (result_type)))
+ return NULL_TREE;
+
+ /* If both operands are signed, don't instrument anything here.
+ Also punt on bit-fields. */
+ if (!INTEGRAL_TYPE_P (op0type) || !INTEGRAL_TYPE_P (op1type)
+ || (!TYPE_OVERFLOW_WRAPS (op0type) && !TYPE_OVERFLOW_WRAPS (op1type))
+ || GET_MODE_BITSIZE (TYPE_MODE (op0type)) != TYPE_PRECISION (op0type)
+ || GET_MODE_BITSIZE (TYPE_MODE (op1type)) != TYPE_PRECISION (op1type))
+ return NULL_TREE;
+
+ switch (code)
+ {
+ case MULT_EXPR:
+ case MINUS_EXPR:
+ case PLUS_EXPR:
+ /* Transform
+ i = u {+,-,*} 5;
+ into
+ i = ISAN_CHECK_{ADD,SUB,MUL} (u, 5); */
+ return build_call_expr_internal_loc (loc, code == PLUS_EXPR
+ ? IFN_ISAN_CHECK_ADD
+ : code == MINUS_EXPR
+ ? IFN_ISAN_CHECK_SUB
+ : IFN_ISAN_CHECK_MUL,
+ result_type, 5, op0, op1,
+ op0_uns_p, op1_uns_p,
+ res_uns_p);
+ default:
+ break;
+ }
+ return NULL_TREE;
+}
+
+/* Return TRUE iff FN is ISAN_CHECK_{ADD,SUB,MUL} internal function. */
+
+bool
+isan_internal_fn_p (tree fn)
+{
+ if (!fn || TREE_CODE (fn) != CALL_EXPR)
+ return false;
+
+ enum internal_fn ifn = CALL_EXPR_IFN (fn);
+ return ifn == IFN_ISAN_CHECK_ADD || ifn == IFN_ISAN_CHECK_SUB
+ ||ifn == IFN_ISAN_CHECK_MUL;
+}
+
+/* Change fourth (RES_UNS_P) parameter of ISAN_CHECK_{ADD,SUB,MUL} function
+ to UNS_P value. This is an extremely hacky way to avoid false psoitive
+ reports, because in many cases we simply cannot set RES_UNS_P value in
+ isan_maybe_instrument_ui correctly since we dont' see LHS and its type. */
+
+void
+isan_maybe_change_ifn_sign_arg (tree *call, int uns_p)
+{
+ tree *opp = &CALL_EXPR_ARG (*call, 4);
+ tree op_new = build_int_cst (integer_type_node, uns_p);
+ *opp = op_new;
+}
extern tree ubsan_instrument_division (location_t, tree, tree);
extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
+extern tree isan_maybe_instrument_ui (location_t, enum tree_code,
+ tree, tree, tree, int, int);
extern tree ubsan_instrument_vla (location_t, tree);
extern tree ubsan_instrument_return (location_t);
extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
/* Declare this here as well as in ubsan.h. */
extern bool do_ubsan_in_current_function (void);
+extern bool isan_internal_fn_p (tree fn);
+extern void isan_maybe_change_ifn_sign_arg (tree *, int);
#endif /* GCC_C_UBSAN_H */
#include "varasm.h"
#include "trans-mem.h"
#include "c-family/c-pragma.h"
+#include "c-family/c-ubsan.h"
#include "c-lang.h"
#include "c-family/c-objc.h"
#include "plugin.h"
static GTY (()) c_parser *the_parser;
+bool processing_static_variable;
+
/* Read in and lex a single token, storing it in *TOKEN. */
static void
its initializer is parsed. */
d = start_decl (declarator, specs, true,
chainon (postfix_attrs, all_prefix_attrs));
+
+ if (d && TREE_STATIC (d))
+ processing_static_variable = true;
+
if (!d)
d = error_mark_node;
if (omp_declare_simd_clauses.exists ()
start_init (d, asm_name, global_bindings_p ());
init_loc = c_parser_peek_token (parser)->location;
init = c_parser_initializer (parser);
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (init.value))
+ isan_maybe_change_ifn_sign_arg (&init.value,
+ TYPE_UNSIGNED (TREE_TYPE (d)));
finish_init ();
+ processing_static_variable = false;
}
if (oacc_routine_clauses)
c_finish_oacc_routine (parser, d, oacc_routine_clauses,
location_t xloc = c_parser_peek_token (parser)->location;
struct c_expr expr = c_parser_expression_conv (parser);
mark_exp_read (expr.value);
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (expr.value))
+ {
+ int uns_p = TYPE_UNSIGNED (TREE_TYPE (DECL_RESULT (current_function_decl)));
+ isan_maybe_change_ifn_sign_arg (&expr.value, uns_p);
+ }
stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc),
expr.value, expr.original_type);
goto expect_semicolon;
if expr.original_code == SIZEOF_EXPR. */
tree c_last_sizeof_arg;
+extern bool processing_static_variable;
+
/* Nonzero if we might need to print a "missing braces around
initializer" message within this initializer. */
static int found_missing_braces;
if (nargs < 0)
return error_mark_node;
+ for (int i = 0; i < nargs; ++i)
+ {
+ tree tmp = (*params)[i];
+ if (TREE_CODE (tmp) == NOP_EXPR)
+ {
+ int real_type_unsigned_p = TYPE_UNSIGNED (TREE_TYPE (tmp));
+ tree arg = TREE_OPERAND (tmp, 0);
+ if (isan_internal_fn_p (arg))
+ {
+ tree *arg_p = &TREE_OPERAND ((*params)[i], 0);
+ isan_maybe_change_ifn_sign_arg (arg_p, real_type_unsigned_p);
+ }
+ }
+ }
+
/* Check that the function is called through a compatible prototype.
If it is not, warn. */
if (CONVERT_EXPR_P (function)
newrhs = rhs;
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (rhs))
+ {
+ tree lhs_type = lhs_origtype ? lhs_origtype : TREE_TYPE (lhs);
+ isan_maybe_change_ifn_sign_arg (&newrhs, TYPE_UNSIGNED (lhs_type));
+ }
+
if (TREE_CODE (lhs) == C_MAYBE_CONST_EXPR)
{
tree inner = build_modify_expr (location, C_MAYBE_CONST_EXPR_EXPR (lhs),
}
newrhs = build_binary_op (location,
modifycode, lhs, newrhs, 1);
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (newrhs))
+ {
+ tree lhs_type = lhs_origtype ? lhs_origtype : TREE_TYPE (lhs);
+ isan_maybe_change_ifn_sign_arg (&newrhs, TYPE_UNSIGNED (lhs_type));
+ }
/* The original type of the right hand side is no longer
meaningful. */
/* Remember whether we're doing << or >>. */
bool doing_shift = false;
+ bool doing_plus = false;
+ bool doing_minus = false;
+ bool doing_mul = false;
+
/* Tree holding instrumentation expression. */
tree instrument_expr = NULL;
goto return_build_binary_op;
}
else
- common = 1;
+ {
+ doing_plus = true;
+ common = 1;
+ }
break;
case MINUS_EXPR:
goto return_build_binary_op;
}
else
- common = 1;
+ {
+ doing_minus = true;
+ common = 1;
+ }
break;
case MULT_EXPR:
+ doing_mul = true;
common = 1;
break;
instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
}
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && (doing_plus || doing_minus || doing_mul)
+ && !processing_static_variable)
+ {
+ op0 = c_save_expr (op0);
+ op1 = c_save_expr (op1);
+ op0 = c_fully_fold (op0, false, NULL);
+ op1 = c_fully_fold (op1, false, NULL);
+ instrument_expr = isan_maybe_instrument_ui (location, code,
+ result_type, op0, op1,
+ TYPE_UNSIGNED (orig_type0),
+ TYPE_UNSIGNED (orig_type1));
+ }
+
/* Treat expressions in initializers specially as they can't trap. */
if (int_const_or_overflow)
ret = (require_constant_value
protected_set_expr_location (ret, location);
if (instrument_expr != NULL)
- ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret),
- instrument_expr, ret);
-
+ {
+ if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+ {
+ /* Inherit side effects. TODO: what another attributes should be
+ inherited? */
+ TREE_SIDE_EFFECTS (instrument_expr) = TREE_SIDE_EFFECTS (ret);
+ ret = instrument_expr;
+ }
+ else
+ ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret),
+ instrument_expr, ret);
+ }
return ret;
}
int flags;
tree decl = get_callee_fndecl (t);
+ /* We may have UBSan internal functions here. */
+ if (!CALL_EXPR_FN (t))
+ return ECF_CONST | ECF_LEAF | ECF_NOTHROW;
+
if (decl)
flags = flags_from_decl_or_type (decl);
else if (CALL_EXPR_FN (t) == NULL_TREE)
#include "convert.h"
#include "langhooks.h"
#include "c-family/c-objc.h"
+#include "c-family/c-ubsan.h"
#include "internal-fn.h"
/* The various kinds of conversion. */
#include "dumpfile.h"
#include "gimplify.h"
#include "intl.h"
+#include "c-family/c-ubsan.h"
/* The number of nested classes being processed. If we are not in the
scope of any class, this is zero. */
return RECUR (TREE_OPERAND (instance, 0));
case CALL_EXPR:
+ if (isan_internal_fn_p (instance))
+ return RECUR (TREE_OPERAND (instance, 0));
/* This is a call to a constructor, hence it's never zero. */
if (TREE_HAS_CONSTRUCTOR (instance))
{
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
return true;
+ case IFN_ISAN_CHECK_ADD:
+ case IFN_ISAN_CHECK_SUB:
+ case IFN_ISAN_CHECK_MUL:
+ return false;
default:
break;
}
#include "intl.h"
#include "decl.h"
#include "c-family/c-objc.h"
+#include "c-family/c-ubsan.h"
#include "plugin.h"
#include "tree-pretty-print.h"
#include "parser.h"
cp_expr rhs = cp_parser_initializer_clause (parser,
&non_constant_p);
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (rhs))
+ {
+ int uns_p = TYPE_UNSIGNED (TREE_TYPE (expr));
+ tree rhs_value = rhs.get_value();
+ isan_maybe_change_ifn_sign_arg (&rhs_value, uns_p);
+ }
+
if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
/* If the next token is a `;', then there is no
expression. */
expr = NULL_TREE;
+
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (expr))
+ {
+ int uns_p = TYPE_UNSIGNED (TREE_TYPE
+ (DECL_RESULT (current_function_decl)));
+ isan_maybe_change_ifn_sign_arg (&expr, uns_p);
+ }
+
/* Build the return-statement. */
statement = finish_return_stmt (expr);
/* Look for the final `;'. */
finish_lambda_scope ();
if (initializer == error_mark_node)
cp_parser_skip_to_end_of_statement (parser);
+ else if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (initializer))
+ {
+ int uns_p = TYPE_UNSIGNED (TREE_TYPE (decl));
+ isan_maybe_change_ifn_sign_arg (&initializer, uns_p);
+ }
}
}
case CALL_EXPR:
{
+ if (!CALL_EXPR_FN (expression))
+ return false;
tree fn = get_callee_fndecl (expression);
int i, nargs;
if (!fn && value_dependent_expression_p (CALL_EXPR_FN (expression)))
case CALL_EXPR:
/* Treat calls to function concepts as dependent. */
- if (function_concept_check_p (*tp))
+ if (CALL_EXPR_FN (*tp) && function_concept_check_p (*tp))
return *tp;
break;
tree result, result_ovl;
tree orig_type = NULL;
+ tree orig_type0 = TREE_TYPE (orig_op0);
+ tree orig_type1 = TREE_TYPE (orig_op1);
+
/* Nonzero if this is an operation like MIN or MAX which can
safely be computed in short if both args are promoted shorts.
Also implies COMMON.
/* Remember whether we're doing << or >>. */
bool doing_shift = false;
+ bool doing_plus = false;
+ bool doing_minus = false;
+ bool doing_mul = false;
+
/* Tree holding instrumentation expression. */
tree instrument_expr = NULL;
else if (!(code0 == POINTER_TYPE && code1 == INTEGER_TYPE))
{
common = 1;
+ doing_minus = 1;
break;
}
/* The pointer - int case is just like pointer + int; fall
complain);
}
common = 1;
+ doing_plus = 1;
break;
case MULT_EXPR:
common = 1;
+ doing_mul = 1;
break;
case TRUNC_DIV_EXPR:
instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
}
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && (doing_plus || doing_minus || doing_mul)
+ && !processing_template_decl)
+ {
+ op0 = cp_save_expr (op0);
+ op1 = cp_save_expr (op1);
+ op0 = fold_non_dependent_expr (op0);
+ op1 = fold_non_dependent_expr (op1);
+ instrument_expr = isan_maybe_instrument_ui (location, code,
+ result_type, op0, op1,
+ TYPE_UNSIGNED (orig_type0),
+ TYPE_UNSIGNED (orig_type1));
+ }
+
result = build2_loc (location, resultcode, build_type, op0, op1);
if (final_type != 0)
result = cp_convert (final_type, result, complain);
if (instrument_expr != NULL)
- result = build2 (COMPOUND_EXPR, TREE_TYPE (result),
- instrument_expr, result);
+ {
+ if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+ {
+ /* Inherit side effects. TODO: what another attributes should be
+ inherited? */
+ TREE_SIDE_EFFECTS (instrument_expr) = TREE_SIDE_EFFECTS (result);
+ result = instrument_expr;
+ }
+ else
+ result = fold_build2 (COMPOUND_EXPR, TREE_TYPE (result),
+ instrument_expr, result);
+ }
if (!processing_template_decl)
{
newrhs = cp_build_binary_op (input_location,
modifycode, lhs, rhs,
complain);
+
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+ && isan_internal_fn_p (newrhs))
+ {
+ int uns_p = TYPE_UNSIGNED (TREE_TYPE (lhs));
+ isan_maybe_change_ifn_sign_arg (&newrhs, uns_p);
+ }
+
if (newrhs == error_mark_node)
{
if (complain & tf_error)
a++;
@end smallexample
+@item -fsanitize=unsigned-integer-overflow
+@opindex fsanitize=unsigned-integer-overflow
+This option enables unsigned and mixed (signed/unsigned) integer overflow
+checking. We check that the result of @code{+}, @code{*}, and binary @code{-}
+does not overflow in the unsigned or mixed arithmetics. These checks can
+trigger false positive warnings on code that intentionally relies on overflows,
+e.g. when computing hash codes.
+
@item -fsanitize=bounds
@opindex fsanitize=bounds
This option enables instrumentation of array bounds. Various out of bounds
SANITIZE_OBJECT_SIZE = 1UL << 20,
SANITIZE_VPTR = 1UL << 21,
SANITIZE_BOUNDS_STRICT = 1UL << 22,
+ SANITIZE_UI_OVERFLOW = 1 << 23,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
| SANITIZE_RETURNS_NONNULL_ATTRIBUTE
| SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
- | SANITIZE_BOUNDS_STRICT
+ | SANITIZE_BOUNDS_STRICT | SANITIZE_UI_OVERFLOW
};
/* flag_vtable_verify initialization levels. */
switch (TREE_CODE (t))
{
case INTEGER_CST:
- if (INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type))
+ if (INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)
+ && !TYPE_OVERFLOW_SANITIZED (type))
return true;
/* Check that -CST will not overflow type. */
if (TREE_OVERFLOW (tem) == TREE_OVERFLOW (t)
|| (ANY_INTEGRAL_TYPE_P (type)
&& !TYPE_OVERFLOW_TRAPS (type)
- && TYPE_OVERFLOW_WRAPS (type))
- || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
+ && !TYPE_OVERFLOW_SANITIZED (type))
+ || (flag_sanitize & (SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW)) == 0)
return tem;
break;
return tem;
}
- goto associate;
+ /* Associate if we don't sanitize unsigned integer overflows. */
+ if ((flag_sanitize & SANITIZE_UI_OVERFLOW) == 0)
+ goto associate;
+
+ return NULL_TREE;
case MULT_EXPR:
if (! FLOAT_TYPE_P (type))
enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
auto_vec<tree> vargs (nargs);
+ /* Ugh, sometimes FE can optimize conditional thus it doesn't
+ depend on ISAN_*_CHECK return value. Don't fold the check in this
+ case, but we'll probably want introduce temporary here. */
+ if (ifn == IFN_ISAN_CHECK_ADD || ifn == IFN_ISAN_CHECK_SUB
+ || ifn == IFN_ISAN_CHECK_MUL)
+ return GS_ALL_DONE;
+
for (i = 0; i < nargs; i++)
{
gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
int prec = GET_MODE_PRECISION (mode);
rtx sgn = immed_wide_int_const (wi::min_value (prec, SIGNED), mode);
bool do_xor = false;
-
- if (is_ubsan)
- gcc_assert (!unsr_p && !uns0_p && !uns1_p);
+ tree orig_arg0 = arg0;
+ tree orig_arg1 = arg1;
if (lhs)
{
{
/* Expand the ubsan builtin call. */
push_temp_slots ();
- fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (arg0),
- arg0, arg1);
+ fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (lhs),
+ orig_arg0, orig_arg1);
expand_normal (fn);
pop_temp_slots ();
do_pending_stack_adjust ();
if (lhs)
{
if (is_ubsan)
- expand_ubsan_result_store (target, res);
+ {
+ if (do_xor)
+ res = expand_binop (mode, add_optab, res, sgn, NULL_RTX, false,
+ OPTAB_LIB_WIDEN);
+ expand_ubsan_result_store (target, res);
+ }
else
{
if (do_xor)
rtx target = NULL_RTX;
signop sign;
enum insn_code icode;
+ tree orig_arg0 = arg0;
+ tree orig_arg1 = arg1;
done_label = gen_label_rtx ();
do_error = gen_label_rtx ();
write_complex_part (target, const0_rtx, true);
}
- if (is_ubsan)
- gcc_assert (!unsr_p && !uns0_p && !uns1_p);
-
/* We assume both operands and result have the same precision
here (GET_MODE_BITSIZE (mode)), S stands for signed type
with that precision, U for unsigned type with that precision,
NULL, do_main_label, PROB_VERY_LIKELY);
do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ if (!is_ubsan)
+ write_complex_part (target, const1_rtx, true);
emit_label (do_main_label);
goto do_main;
default:
is, thus we can keep do_main code oring in overflow as is. */
do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ if (!is_ubsan)
+ write_complex_part (target, const1_rtx, true);
emit_label (do_main_label);
goto do_main;
default:
{
/* Expand the ubsan builtin call. */
push_temp_slots ();
- fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (arg0),
- arg0, arg1);
+ fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (lhs),
+ orig_arg0, orig_arg1);
expand_normal (fn);
pop_temp_slots ();
do_pending_stack_adjust ();
rtx_code_label *all_done_label = gen_label_rtx ();
do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode, NULL_RTX,
NULL, all_done_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ if (!is_ubsan)
+ write_complex_part (target, const1_rtx, true);
emit_label (all_done_label);
}
rtx_code_label *set_noovf = gen_label_rtx ();
do_compare_rtx_and_jump (op1, const0_rtx, GE, false, mode, NULL_RTX,
NULL, all_done_label, PROB_VERY_LIKELY);
- write_complex_part (target, const1_rtx, true);
+ if (!is_ubsan)
+ write_complex_part (target, const1_rtx, true);
do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, set_noovf, PROB_VERY_LIKELY);
do_compare_rtx_and_jump (op0, constm1_rtx, NE, true, mode, NULL_RTX,
do_compare_rtx_and_jump (op1, res, NE, true, mode, NULL_RTX, NULL,
all_done_label, PROB_VERY_UNLIKELY);
emit_label (set_noovf);
- write_complex_part (target, const0_rtx, true);
+ if (!is_ubsan)
+ write_complex_part (target, const0_rtx, true);
emit_label (all_done_label);
}
}
}
+/* Expand ISAN_CHECK_ADD call STMT. */
+
+static void
+expand_ISAN_CHECK_ADD (internal_fn, gcall *stmt)
+{
+ location_t loc = gimple_location (stmt);
+ tree lhs = gimple_call_lhs (stmt);
+ tree arg0 = gimple_call_arg (stmt, 0);
+ tree arg1 = gimple_call_arg (stmt, 1);
+ int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+ int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+ int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+ expand_addsub_overflow (loc, PLUS_EXPR, lhs, arg0, arg1,
+ unsr_p, uns0_p, uns1_p, true);
+}
+
+/* Expand ISAN_CHECK_SUB call STMT. */
+
+static void
+expand_ISAN_CHECK_SUB (internal_fn, gcall *stmt)
+{
+ location_t loc = gimple_location (stmt);
+ tree lhs = gimple_call_lhs (stmt);
+ tree arg0 = gimple_call_arg (stmt, 0);
+ tree arg1 = gimple_call_arg (stmt, 1);
+ int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+ int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+ int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+ if (integer_zerop (arg0))
+ expand_neg_overflow (loc, lhs, arg1, true);
+ else
+ expand_addsub_overflow (loc, MINUS_EXPR, lhs, arg0, arg1,
+ unsr_p, uns0_p, uns1_p, true);
+}
+
+/* Expand ISAN_CHECK_MUL call STMT. */
+
+static void
+expand_ISAN_CHECK_MUL (internal_fn, gcall *stmt)
+{
+ location_t loc = gimple_location (stmt);
+ tree lhs = gimple_call_lhs (stmt);
+ tree arg0 = gimple_call_arg (stmt, 0);
+ tree arg1 = gimple_call_arg (stmt, 1);
+ int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+ int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+ int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+ expand_mul_overflow (loc, lhs, arg0, arg1, uns0_p, uns1_p, unsr_p, true);
+}
+
/* Expand UBSAN_CHECK_ADD call STMT. */
static void
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
SANITIZER_OPT (return, SANITIZE_RETURN),
SANITIZER_OPT (null, SANITIZE_NULL),
SANITIZER_OPT (signed-integer-overflow, SANITIZE_SI_OVERFLOW),
+ SANITIZER_OPT (unsigned-integer-overflow, SANITIZE_UI_OVERFLOW),
SANITIZER_OPT (bool, SANITIZE_BOOL),
SANITIZER_OPT (enum, SANITIZE_ENUM),
SANITIZER_OPT (float-divide-by-zero, SANITIZE_FLOAT_DIVIDE),
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+__extension__ typedef signed long long gint64;
+__extension__ typedef unsigned long long guint64;
+typedef signed int gint32;
+typedef unsigned int guint32;
+typedef int gint;
+
+static void add_uint32 (guint32 *out, guint32 *in, gint bytes)
+{
+ gint i;
+ for (i = 0; i < bytes / sizeof (guint32); i++)
+ out[i] = ((((guint64)out[i] + (guint64)in[i]) > 1) ? 111111111 : (((guint64)out[i] + (guint64)in[i]) < 0) ? 0 : 1111111);
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -fdump-tree-gimple" } */
+
+extern int *PL_tmps_stack;
+
+void foo (int items, int tmpsbase) {
+ int i = items;
+ while (i-- > 0)
+ PL_tmps_stack[--tmpsbase] |= 3;
+ return;
+}
+
+/* { dg-final { scan-tree-dump-times "ISAN_CHECK_ADD" 1 "gimple" } } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+#define CHECK_ADD(type1, type2, type3, val1, val2) \
+ do { \
+ volatile type1 a = val1; \
+ volatile type2 b = val2; \
+ volatile type3 c = a + b; \
+ asm volatile ("" : : "r" (c) : "memory"); \
+ } while (0)
+
+int main () {
+
+ CHECK_ADD(uint32_t, uint32_t, uint32_t, UINT32_MAX, 1);
+ CHECK_ADD(uint32_t, uint32_t, int32_t, UINT32_MAX, 1);
+ CHECK_ADD(uint32_t, int32_t, uint32_t, UINT32_MAX, 1);
+ CHECK_ADD(uint32_t, int32_t, int32_t, UINT32_MAX, 1);
+ CHECK_ADD(uint32_t, uint32_t, uint32_t, INT32_MAX, 1);
+ CHECK_ADD(uint32_t, int32_t, int32_t, INT32_MAX, -1);
+
+ CHECK_ADD(uint64_t, uint64_t, uint64_t, UINT64_MAX, 1);
+ CHECK_ADD(uint64_t, uint64_t, int64_t, UINT64_MAX, 1);
+ CHECK_ADD(uint64_t, int64_t, uint64_t, UINT64_MAX, 1);
+ CHECK_ADD(uint64_t, int64_t, int64_t, UINT64_MAX, 1);
+ CHECK_ADD(uint64_t, uint64_t, uint64_t, INT64_MAX, 1);
+ CHECK_ADD(uint64_t, int64_t, uint64_t, INT64_MAX, -1);
+
+ CHECK_ADD(int8_t *, unsigned, int8_t *, (int8_t *) UINT64_MAX, 1);
+ CHECK_ADD(int32_t *, unsigned, int32_t *, (int32_t *) UINT64_MAX, 1);
+ CHECK_ADD(int64_t *, unsigned, int64_t *, (int64_t *) UINT64_MAX, 1);
+ CHECK_ADD(int8_t *, unsigned, int8_t *, (int8_t *) SIZE_MAX, 1);
+ CHECK_ADD(int8_t *, signed, int8_t *, (int8_t *) SIZE_MAX, -1);
+
+ return 0;
+}
+
+
+/* { dg-output "unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 4 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 8 cannot be represented in type '.*'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <limits.h>
+
+#define CHECK_MUL(sign1, sign2, sign3, type1, type2, type3, val1, val2) \
+ do { \
+ volatile sign1 type1 a = val1; \
+ volatile sign2 type2 b = val2; \
+ volatile sign3 type3 c = a * b; \
+ } while (0)
+
+int main () {
+
+ CHECK_MUL(unsigned, unsigned, unsigned, int, int, int, 2, UINT_MAX);
+ CHECK_MUL(unsigned, unsigned, signed, int, int, int, 2, UINT_MAX);
+ CHECK_MUL(signed, unsigned, unsigned, int, int, int, 2, UINT_MAX);
+ CHECK_MUL(signed, unsigned, signed, int, int, int, 2, UINT_MAX);
+ CHECK_MUL(unsigned, unsigned, unsigned, int, int, int, 2, INT_MAX);
+ CHECK_MUL(signed, signed, signed, int, int, int, -2, INT_MAX);
+
+ CHECK_MUL(unsigned, unsigned, unsigned, long, long, long, 2, ULONG_MAX);
+ CHECK_MUL(unsigned, unsigned, signed, long, long, long, 2, ULONG_MAX);
+ CHECK_MUL(signed, unsigned, unsigned, long, long, long, 2, ULONG_MAX);
+ CHECK_MUL(signed, unsigned, signed, long, long, long, 2, ULONG_MAX);
+ CHECK_MUL(unsigned, unsigned, unsigned, long, long, long, 2, LONG_MAX);
+ CHECK_MUL(signed, signed, signed, long, long, long, -2, LONG_MAX);
+
+ return 0;
+}
+
+/* { dg-output "unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+#define CHECK_SUB(type1, type2, type3, val1, val2) \
+ do { \
+ volatile type1 a = val1; \
+ volatile type2 b = val2; \
+ volatile type3 c = a - b; \
+ asm volatile ("" : : "r" (c) : "memory"); \
+ } while (0)
+
+int main () {
+
+ CHECK_SUB(uint32_t, uint32_t, uint32_t, 1, UINT32_MAX);
+ CHECK_SUB(uint32_t, uint32_t, int32_t, 1, UINT32_MAX);
+ CHECK_SUB(int32_t, uint32_t, uint32_t, 1, UINT32_MAX);
+ CHECK_SUB(int32_t, uint32_t, int32_t, 1, UINT32_MAX);
+ CHECK_SUB(uint32_t, uint32_t, uint32_t, 1, 1);
+ CHECK_SUB(uint32_t, int32_t, uint32_t, 1, -1);
+
+ CHECK_SUB(uint64_t, uint64_t, uint64_t, 1, UINT64_MAX);
+ CHECK_SUB(uint64_t, uint64_t, int64_t, 1, UINT64_MAX);
+ CHECK_SUB(int64_t, uint64_t, uint64_t, 1, UINT64_MAX);
+ CHECK_SUB(int64_t, uint64_t, int64_t, 1, UINT64_MAX);
+ CHECK_SUB(uint64_t, uint64_t, uint64_t, 1, 1);
+ CHECK_SUB(uint64_t, int64_t, uint64_t, 1, -1);
+
+ CHECK_SUB(int8_t *, unsigned, int8_t *, (int8_t *) 1, 2);
+ CHECK_SUB(int32_t *, unsigned, int32_t *, (int32_t *) 1, 2);
+ CHECK_SUB(int64_t *, unsigned, int64_t *, (int64_t *) 1, 2);
+ CHECK_SUB(int8_t *, unsigned, int8_t *, (int8_t *) 1, 1);
+ CHECK_SUB(int8_t *, signed, int8_t *, (int8_t *) 1, -1);
+
+ return 0;
+}
+
+/* { dg-output "unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 2 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 8 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 16 cannot be represented in type '.*'" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -fdump-tree-gimple" } */
+
+char a;
+
+void foo () {
+ static char *b = &a + 14;
+ char *c = &a + 14;
+}
+
+/* { dg-final { scan-tree-dump "ISAN_CHECK_ADD" "gimple" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <string>
+
+extern void bar ();
+
+void foo (std::string &s) {
+ for (int i = 0; i < 20 - s.substr(1, 6).length(); ++i)
+ bar ();
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -std=gnu++11" } */
+
+#include <algorithm>
+
+constexpr int foo (int i) {
+ return std::__lg (i);
+}
--- /dev/null
+# Copyright (C) 2013-2015 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib ubsan-dg.exp
+
+# Initialize `dg'.
+dg-init
+ubsan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_undefined] {
+ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/isan/*.c]] "" ""
+}
+
+# All done.
+ubsan_finish
+dg-finish
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+/* { dg-set-target-env-var UBSAN_OPTIONS "halt_on_error=1" } */
+
+int foo (int c) {
+};
+
+int bar (unsigned a, unsigned b) {
+ return a - b;
+}
+
+int main() {
+ unsigned a = 1;
+ unsigned b = 2;
+
+ int c = a - b;
+ foo (a - b);
+ c = bar (a, b);
+}
--- /dev/null
+# Copyright (C) 2013-2015 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib ubsan-dg.exp
+
+# Initialize `dg'.
+dg-init
+ubsan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_undefined] {
+ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/isan/*.c]] "" ""
+}
+
+# All done.
+ubsan_finish
+dg-finish
mask = fold_build1 (BIT_NOT_EXPR, type, tem1);
tem1 = fold_binary (MINUS_EXPR, type,
fold_convert (type, rangei->exp), lowi);
+ if (tem1 == NULL_TREE)
+ return false;
tem1 = fold_build2 (BIT_AND_EXPR, type, tem1, mask);
lowj = build_int_cst (type, 0);
if (update_range_test (rangei, rangej, NULL, 1, opcode, ops, tem1,
/* True if an overflow is to be preserved for sanitization. */
#define TYPE_OVERFLOW_SANITIZED(TYPE) \
(INTEGRAL_TYPE_P (TYPE) \
- && !TYPE_OVERFLOW_WRAPS (TYPE) \
- && (flag_sanitize & SANITIZE_SI_OVERFLOW))
+ && (flag_sanitize & (SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW)))
/* True if pointer types have undefined overflow. */
#define POINTER_TYPE_OVERFLOW_UNDEFINED (flag_strict_overflow)
ubsan_type_descriptor (lhstype), NULL_TREE,
NULL_TREE);
enum built_in_function fn_code;
+ bool uns_p = TYPE_UNSIGNED (lhstype);
+ unsigned sanitize_code = uns_p ? SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW
+ : SANITIZE_SI_OVERFLOW;
switch (code)
{
case PLUS_EXPR:
- fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ fn_code = (flag_sanitize_recover & sanitize_code)
? BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW
: BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT;
break;
case MINUS_EXPR:
- fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ fn_code = (flag_sanitize_recover & sanitize_code)
? BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW
: BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT;
break;
case MULT_EXPR:
- fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ fn_code = (flag_sanitize_recover & sanitize_code)
? BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW
: BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT;
break;
case NEGATE_EXPR:
- fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ fn_code = (flag_sanitize_recover & sanitize_code)
? BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW
: BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT;
break;
virtual bool gate (function *)
{
return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+ | SANITIZE_UI_OVERFLOW
| SANITIZE_BOOL | SANITIZE_ENUM
| SANITIZE_ALIGNMENT
| SANITIZE_NONNULL_ATTRIBUTE