Implement -fsanitize=signed-integer-overflow.
From-SVN: r205684
This commit is contained in:
parent
59b6687cce
commit
31e071aeb8
@ -1,3 +1,50 @@
|
||||
2013-12-04 Jakub Jelinek <jakub@redhat.com>
|
||||
Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* opts.c (common_handle_option): Handle
|
||||
-fsanitize=signed-integer-overflow.
|
||||
* config/i386/i386.md (addv<mode>4, subv<mode>4, mulv<mode>4,
|
||||
negv<mode>3, negv<mode>3_1): Define expands.
|
||||
(*addv<mode>4, *subv<mode>4, *mulv<mode>4, *negv<mode>3): Define
|
||||
insns.
|
||||
* sanitizer.def (BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW,
|
||||
BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW,
|
||||
BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW,
|
||||
BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW): Define.
|
||||
* ubsan.h (PROB_VERY_UNLIKELY, PROB_EVEN, PROB_VERY_LIKELY,
|
||||
PROB_ALWAYS): Define.
|
||||
(ubsan_build_overflow_builtin): Declare.
|
||||
* gimple-fold.c (gimple_fold_stmt_to_constant_1): Add folding of
|
||||
internal functions.
|
||||
* ubsan.c (PROB_VERY_UNLIKELY): Don't define here.
|
||||
(ubsan_build_overflow_builtin): New function.
|
||||
(instrument_si_overflow): Likewise.
|
||||
(ubsan_pass): Add signed integer overflow checking.
|
||||
(gate_ubsan): Enable the pass also when SANITIZE_SI_OVERFLOW.
|
||||
* flag-types.h (enum sanitize_code): Add SANITIZE_SI_OVERFLOW.
|
||||
* internal-fn.c: Include ubsan.h and target.h.
|
||||
(ubsan_expand_si_overflow_addsub_check): New function.
|
||||
(ubsan_expand_si_overflow_neg_check): Likewise.
|
||||
(ubsan_expand_si_overflow_mul_check): Likewise.
|
||||
(expand_UBSAN_CHECK_ADD): Likewise.
|
||||
(expand_UBSAN_CHECK_SUB): Likewise.
|
||||
(expand_UBSAN_CHECK_MUL): Likewise.
|
||||
* fold-const.c (fold_binary_loc): Don't fold A + (-B) -> A - B and
|
||||
(-A) + B -> B - A when doing the signed integer overflow checking.
|
||||
* internal-fn.def (UBSAN_CHECK_ADD, UBSAN_CHECK_SUB, UBSAN_CHECK_MUL):
|
||||
Define.
|
||||
* tree-vrp.c (extract_range_basic): Handle internal calls.
|
||||
* optabs.def (addv4_optab, subv4_optab, mulv4_optab, negv4_optab): New
|
||||
optabs.
|
||||
* asan.c: Include predict.h.
|
||||
(PROB_VERY_UNLIKELY, PROB_ALWAYS): Don't define here.
|
||||
* predict.c: Move the PROB_* macros...
|
||||
* predict.h (enum br_predictor): ...here.
|
||||
(PROB_LIKELY, PROB_UNLIKELY): Define.
|
||||
* trans-mem.c: Include predict.h.
|
||||
(PROB_VERY_UNLIKELY, PROB_ALWAYS, PROB_VERY_LIKELY,
|
||||
PROB_LIKELY, PROB_UNLIKELY): Don't define here.
|
||||
|
||||
2013-12-04 Jeff Law <law@redhat.com>
|
||||
|
||||
* expr.c (expand_assignment): Update comments.
|
||||
|
@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cfgloop.h"
|
||||
#include "gimple-builder.h"
|
||||
#include "ubsan.h"
|
||||
#include "predict.h"
|
||||
|
||||
/* AddressSanitizer finds out-of-bounds and use-after-free bugs
|
||||
with <2x slowdown on average.
|
||||
@ -1311,9 +1312,6 @@ report_error_func (bool is_store, int size_in_bytes)
|
||||
return builtin_decl_implicit (report[is_store][exact_log2 (size_in_bytes)]);
|
||||
}
|
||||
|
||||
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
|
||||
#define PROB_ALWAYS (REG_BR_PROB_BASE)
|
||||
|
||||
/* Split the current basic block and create a condition statement
|
||||
insertion point right before or after the statement pointed to by
|
||||
ITER. Return an iterator to the point at which the caller might
|
||||
|
@ -1,3 +1,9 @@
|
||||
2013-12-04 Jakub Jelinek <jakub@redhat.com>
|
||||
Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* c-gimplify.c (c_gimplify_expr): If doing the integer-overflow
|
||||
sanitization, call unsigned_type_for only when !TYPE_OVERFLOW_WRAPS.
|
||||
|
||||
2013-11-29 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
PR c/59309
|
||||
|
@ -199,7 +199,9 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED,
|
||||
tree type = TREE_TYPE (TREE_OPERAND (*expr_p, 0));
|
||||
if (INTEGRAL_TYPE_P (type) && c_promoting_integer_type_p (type))
|
||||
{
|
||||
if (TYPE_OVERFLOW_UNDEFINED (type))
|
||||
if (TYPE_OVERFLOW_UNDEFINED (type)
|
||||
|| ((flag_sanitize & SANITIZE_SI_OVERFLOW)
|
||||
&& !TYPE_OVERFLOW_WRAPS (type)))
|
||||
type = unsigned_type_for (type);
|
||||
return gimplify_self_mod_expr (expr_p, pre_p, post_p, 1, type);
|
||||
}
|
||||
|
@ -905,8 +905,8 @@
|
||||
(TI "TARGET_64BIT")])
|
||||
|
||||
;; Double word integer modes as mode attribute.
|
||||
(define_mode_attr DWI [(SI "DI") (DI "TI")])
|
||||
(define_mode_attr dwi [(SI "di") (DI "ti")])
|
||||
(define_mode_attr DWI [(QI "HI") (HI "SI") (SI "DI") (DI "TI")])
|
||||
(define_mode_attr dwi [(QI "hi") (HI "si") (SI "di") (DI "ti")])
|
||||
|
||||
;; Half mode for double word integer modes.
|
||||
(define_mode_iterator DWIH [(SI "!TARGET_64BIT")
|
||||
@ -6160,6 +6160,41 @@
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "mode" "QI")])
|
||||
|
||||
;; Add with jump on overflow.
|
||||
(define_expand "addv<mode>4"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (plus:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 1 "nonimmediate_operand"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 2 "<general_operand>")))
|
||||
(sign_extend:<DWI>
|
||||
(plus:SWI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI 0 "register_operand")
|
||||
(plus:SWI (match_dup 1) (match_dup 2)))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))]
|
||||
""
|
||||
"ix86_fixup_binary_operands_no_copy (PLUS, <MODE>mode, operands);")
|
||||
|
||||
(define_insn "*addv<mode>4"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (plus:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 1 "nonimmediate_operand" "%0,0"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 2 "<general_operand>" "<g>,<r><i>")))
|
||||
(sign_extend:<DWI>
|
||||
(plus:SWI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>,<r>m")
|
||||
(plus:SWI (match_dup 1) (match_dup 2)))]
|
||||
"ix86_binary_operator_ok (PLUS, <MODE>mode, operands)"
|
||||
"add{<imodesuffix>}\t{%2, %0|%0, %2}"
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
;; The lea patterns for modes less than 32 bits need to be matched by
|
||||
;; several insns converted to real lea by splitters.
|
||||
|
||||
@ -6397,6 +6432,41 @@
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "mode" "SI")])
|
||||
|
||||
;; Subtract with jump on overflow.
|
||||
(define_expand "subv<mode>4"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (minus:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 1 "nonimmediate_operand"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 2 "<general_operand>")))
|
||||
(sign_extend:<DWI>
|
||||
(minus:SWI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI 0 "register_operand")
|
||||
(minus:SWI (match_dup 1) (match_dup 2)))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))]
|
||||
""
|
||||
"ix86_fixup_binary_operands_no_copy (MINUS, <MODE>mode, operands);")
|
||||
|
||||
(define_insn "*subv<mode>4"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (minus:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 1 "nonimmediate_operand" "0,0"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 2 "<general_operand>" "<r><i>,<r>m")))
|
||||
(sign_extend:<DWI>
|
||||
(minus:SWI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m,<r>")
|
||||
(minus:SWI (match_dup 1) (match_dup 2)))]
|
||||
"ix86_binary_operator_ok (MINUS, <MODE>mode, operands)"
|
||||
"sub{<imodesuffix>}\t{%2, %0|%0, %2}"
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
(define_insn "*sub<mode>_3"
|
||||
[(set (reg FLAGS_REG)
|
||||
(compare (match_operand:SWI 1 "nonimmediate_operand" "0,0")
|
||||
@ -6711,6 +6781,58 @@
|
||||
(set_attr "bdver1_decode" "direct")
|
||||
(set_attr "mode" "QI")])
|
||||
|
||||
;; Multiply with jump on overflow.
|
||||
(define_expand "mulv<mode>4"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI48 1 "register_operand"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI48 2 "<general_operand>")))
|
||||
(sign_extend:<DWI>
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI48 0 "register_operand")
|
||||
(mult:SWI48 (match_dup 1) (match_dup 2)))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 3))
|
||||
(pc)))])
|
||||
|
||||
(define_insn "*mulv<mode>4"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(eq:CCO (mult:<DWI>
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 1 "nonimmediate_operand" "%rm,rm,0"))
|
||||
(sign_extend:<DWI>
|
||||
(match_operand:SWI 2 "<general_operand>" "K,<i>,mr")))
|
||||
(sign_extend:<DWI>
|
||||
(mult:SWI (match_dup 1) (match_dup 2)))))
|
||||
(set (match_operand:SWI 0 "register_operand" "=r,r,r")
|
||||
(mult:SWI (match_dup 1) (match_dup 2)))]
|
||||
"!(MEM_P (operands[1]) && MEM_P (operands[2]))"
|
||||
"@
|
||||
imul{<imodesuffix>}\t{%2, %1, %0|%0, %1, %2}
|
||||
imul{<imodesuffix>}\t{%2, %1, %0|%0, %1, %2}
|
||||
imul{<imodesuffix>}\t{%2, %0|%0, %2}"
|
||||
[(set_attr "type" "imul")
|
||||
(set_attr "prefix_0f" "0,0,1")
|
||||
(set (attr "athlon_decode")
|
||||
(cond [(eq_attr "cpu" "athlon")
|
||||
(const_string "vector")
|
||||
(eq_attr "alternative" "1")
|
||||
(const_string "vector")
|
||||
(and (eq_attr "alternative" "2")
|
||||
(match_operand 1 "memory_operand"))
|
||||
(const_string "vector")]
|
||||
(const_string "direct")))
|
||||
(set (attr "amdfam10_decode")
|
||||
(cond [(and (eq_attr "alternative" "0,1")
|
||||
(match_operand 1 "memory_operand"))
|
||||
(const_string "vector")]
|
||||
(const_string "direct")))
|
||||
(set_attr "bdver1_decode" "direct")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
(define_expand "<u>mul<mode><dwi>3"
|
||||
[(parallel [(set (match_operand:<DWI> 0 "register_operand")
|
||||
(mult:<DWI>
|
||||
@ -8624,6 +8746,36 @@
|
||||
[(set_attr "type" "negnot")
|
||||
(set_attr "mode" "SI")])
|
||||
|
||||
;; Negate with jump on overflow.
|
||||
(define_expand "negv<mode>3"
|
||||
[(parallel [(set (reg:CCO FLAGS_REG)
|
||||
(ne:CCO (match_operand:SWI 1 "register_operand")
|
||||
(match_dup 3)))
|
||||
(set (match_operand:SWI 0 "register_operand")
|
||||
(neg:SWI (match_dup 1)))])
|
||||
(set (pc) (if_then_else
|
||||
(eq (reg:CCO FLAGS_REG) (const_int 0))
|
||||
(label_ref (match_operand 2))
|
||||
(pc)))]
|
||||
""
|
||||
{
|
||||
operands[3]
|
||||
= gen_int_mode (HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (<MODE>mode) - 1),
|
||||
<MODE>mode);
|
||||
})
|
||||
|
||||
(define_insn "*negv<mode>3"
|
||||
[(set (reg:CCO FLAGS_REG)
|
||||
(ne:CCO (match_operand:SWI 1 "nonimmediate_operand" "0")
|
||||
(match_operand:SWI 2 "const_int_operand")))
|
||||
(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m")
|
||||
(neg:SWI (match_dup 1)))]
|
||||
"ix86_unary_operator_ok (NEG, <MODE>mode, operands)
|
||||
&& mode_signbit_p (<MODE>mode, operands[2])"
|
||||
"neg{<imodesuffix>}\t%0"
|
||||
[(set_attr "type" "negnot")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
;; Changing of sign for FP values is doable using integer unit too.
|
||||
|
||||
(define_expand "<code><mode>2"
|
||||
|
@ -215,8 +215,10 @@ enum sanitize_code {
|
||||
SANITIZE_VLA = 1 << 6,
|
||||
SANITIZE_NULL = 1 << 7,
|
||||
SANITIZE_RETURN = 1 << 8,
|
||||
SANITIZE_SI_OVERFLOW = 1 << 9,
|
||||
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
|
||||
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
|
||||
| SANITIZE_SI_OVERFLOW
|
||||
};
|
||||
|
||||
/* flag_vtable_verify initialization levels. */
|
||||
|
@ -10350,14 +10350,16 @@ fold_binary_loc (location_t loc,
|
||||
|
||||
case PLUS_EXPR:
|
||||
/* A + (-B) -> A - B */
|
||||
if (TREE_CODE (arg1) == NEGATE_EXPR)
|
||||
if (TREE_CODE (arg1) == NEGATE_EXPR
|
||||
&& (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
|
||||
return fold_build2_loc (loc, MINUS_EXPR, type,
|
||||
fold_convert_loc (loc, type, arg0),
|
||||
fold_convert_loc (loc, type,
|
||||
TREE_OPERAND (arg1, 0)));
|
||||
/* (-A) + B -> B - A */
|
||||
if (TREE_CODE (arg0) == NEGATE_EXPR
|
||||
&& reorder_operands_p (TREE_OPERAND (arg0, 0), arg1))
|
||||
&& reorder_operands_p (TREE_OPERAND (arg0, 0), arg1)
|
||||
&& (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
|
||||
return fold_build2_loc (loc, MINUS_EXPR, type,
|
||||
fold_convert_loc (loc, type, arg1),
|
||||
fold_convert_loc (loc, type,
|
||||
|
@ -2660,8 +2660,37 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
|
||||
tree fn;
|
||||
|
||||
if (gimple_call_internal_p (stmt))
|
||||
/* No folding yet for these functions. */
|
||||
return NULL_TREE;
|
||||
{
|
||||
enum tree_code subcode = ERROR_MARK;
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_UBSAN_CHECK_ADD:
|
||||
subcode = PLUS_EXPR;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_SUB:
|
||||
subcode = MINUS_EXPR;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_MUL:
|
||||
subcode = MULT_EXPR;
|
||||
break;
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
tree op0 = (*valueize) (gimple_call_arg (stmt, 0));
|
||||
tree op1 = (*valueize) (gimple_call_arg (stmt, 1));
|
||||
|
||||
if (TREE_CODE (op0) != INTEGER_CST
|
||||
|| TREE_CODE (op1) != INTEGER_CST)
|
||||
return NULL_TREE;
|
||||
tree res = fold_binary_loc (loc, subcode,
|
||||
TREE_TYPE (gimple_call_arg (stmt, 0)),
|
||||
op0, op1);
|
||||
if (res
|
||||
&& TREE_CODE (res) == INTEGER_CST
|
||||
&& !TREE_OVERFLOW (res))
|
||||
return res;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
fn = (*valueize) (gimple_call_fn (stmt));
|
||||
if (TREE_CODE (fn) == ADDR_EXPR
|
||||
|
@ -31,6 +31,9 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "gimple-expr.h"
|
||||
#include "is-a.h"
|
||||
#include "gimple.h"
|
||||
#include "ubsan.h"
|
||||
#include "target.h"
|
||||
#include "predict.h"
|
||||
|
||||
/* The names of each internal function, indexed by function number. */
|
||||
const char *const internal_fn_name_array[] = {
|
||||
@ -153,6 +156,305 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Add sub/add overflow checking to the statement STMT.
|
||||
CODE says whether the operation is +, or -. */
|
||||
|
||||
void
|
||||
ubsan_expand_si_overflow_addsub_check (tree_code code, gimple stmt)
|
||||
{
|
||||
rtx res, op0, op1;
|
||||
tree lhs, fn, arg0, arg1;
|
||||
rtx done_label, do_error, target = NULL_RTX;
|
||||
|
||||
lhs = gimple_call_lhs (stmt);
|
||||
arg0 = gimple_call_arg (stmt, 0);
|
||||
arg1 = gimple_call_arg (stmt, 1);
|
||||
done_label = gen_label_rtx ();
|
||||
do_error = gen_label_rtx ();
|
||||
fn = ubsan_build_overflow_builtin (code, gimple_location (stmt),
|
||||
TREE_TYPE (arg0), arg0, arg1);
|
||||
do_pending_stack_adjust ();
|
||||
op0 = expand_normal (arg0);
|
||||
op1 = expand_normal (arg1);
|
||||
|
||||
enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg0));
|
||||
if (lhs)
|
||||
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
|
||||
|
||||
enum insn_code icode
|
||||
= optab_handler (code == PLUS_EXPR ? addv4_optab : subv4_optab, mode);
|
||||
if (icode != CODE_FOR_nothing)
|
||||
{
|
||||
struct expand_operand ops[4];
|
||||
rtx last = get_last_insn ();
|
||||
|
||||
res = gen_reg_rtx (mode);
|
||||
create_output_operand (&ops[0], res, mode);
|
||||
create_input_operand (&ops[1], op0, mode);
|
||||
create_input_operand (&ops[2], op1, mode);
|
||||
create_fixed_operand (&ops[3], do_error);
|
||||
if (maybe_expand_insn (icode, 4, ops))
|
||||
{
|
||||
last = get_last_insn ();
|
||||
if (profile_status != PROFILE_ABSENT
|
||||
&& JUMP_P (last)
|
||||
&& any_condjump_p (last)
|
||||
&& !find_reg_note (last, REG_BR_PROB, 0))
|
||||
add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY);
|
||||
emit_jump (done_label);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_insns_since (last);
|
||||
icode = CODE_FOR_nothing;
|
||||
}
|
||||
}
|
||||
|
||||
if (icode == CODE_FOR_nothing)
|
||||
{
|
||||
rtx sub_check = gen_label_rtx ();
|
||||
|
||||
/* Compute the operation. On RTL level, the addition is always
|
||||
unsigned. */
|
||||
res = expand_binop (mode, add_optab, op0, op1,
|
||||
NULL_RTX, false, OPTAB_LIB_WIDEN);
|
||||
|
||||
/* If the op1 is negative, we have to use a different check. */
|
||||
emit_cmp_and_jump_insns (op1, const0_rtx, LT, NULL_RTX, mode,
|
||||
false, sub_check, PROB_EVEN);
|
||||
|
||||
/* Compare the result of the addition with one of the operands. */
|
||||
emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? GE : LE,
|
||||
NULL_RTX, mode, false, done_label,
|
||||
PROB_VERY_LIKELY);
|
||||
/* If we get here, we have to print the error. */
|
||||
emit_jump (do_error);
|
||||
|
||||
emit_label (sub_check);
|
||||
/* We have k = a + b for b < 0 here. k <= a must hold. */
|
||||
emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? LE : GE,
|
||||
NULL_RTX, mode, false, done_label,
|
||||
PROB_VERY_LIKELY);
|
||||
}
|
||||
|
||||
emit_label (do_error);
|
||||
/* Expand the ubsan builtin call. */
|
||||
expand_normal (fn);
|
||||
do_pending_stack_adjust ();
|
||||
|
||||
/* We're done. */
|
||||
emit_label (done_label);
|
||||
|
||||
if (lhs)
|
||||
emit_move_insn (target, res);
|
||||
}
|
||||
|
||||
/* Add negate overflow checking to the statement STMT. */
|
||||
|
||||
void
|
||||
ubsan_expand_si_overflow_neg_check (gimple stmt)
|
||||
{
|
||||
rtx res, op1;
|
||||
tree lhs, fn, arg1;
|
||||
rtx done_label, do_error, target = NULL_RTX;
|
||||
|
||||
lhs = gimple_call_lhs (stmt);
|
||||
arg1 = gimple_call_arg (stmt, 1);
|
||||
done_label = gen_label_rtx ();
|
||||
do_error = gen_label_rtx ();
|
||||
fn = ubsan_build_overflow_builtin (NEGATE_EXPR, gimple_location (stmt),
|
||||
TREE_TYPE (arg1), arg1, NULL_TREE);
|
||||
|
||||
do_pending_stack_adjust ();
|
||||
op1 = expand_normal (arg1);
|
||||
|
||||
enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg1));
|
||||
if (lhs)
|
||||
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
|
||||
|
||||
enum insn_code icode = optab_handler (negv3_optab, mode);
|
||||
if (icode != CODE_FOR_nothing)
|
||||
{
|
||||
struct expand_operand ops[3];
|
||||
rtx last = get_last_insn ();
|
||||
|
||||
res = gen_reg_rtx (mode);
|
||||
create_output_operand (&ops[0], res, mode);
|
||||
create_input_operand (&ops[1], op1, mode);
|
||||
create_fixed_operand (&ops[2], do_error);
|
||||
if (maybe_expand_insn (icode, 3, ops))
|
||||
{
|
||||
last = get_last_insn ();
|
||||
if (profile_status != PROFILE_ABSENT
|
||||
&& JUMP_P (last)
|
||||
&& any_condjump_p (last)
|
||||
&& !find_reg_note (last, REG_BR_PROB, 0))
|
||||
add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY);
|
||||
emit_jump (done_label);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_insns_since (last);
|
||||
icode = CODE_FOR_nothing;
|
||||
}
|
||||
}
|
||||
|
||||
if (icode == CODE_FOR_nothing)
|
||||
{
|
||||
/* Compute the operation. On RTL level, the addition is always
|
||||
unsigned. */
|
||||
res = expand_unop (mode, neg_optab, op1, NULL_RTX, false);
|
||||
|
||||
/* Compare the operand with the most negative value. */
|
||||
rtx minv = expand_normal (TYPE_MIN_VALUE (TREE_TYPE (arg1)));
|
||||
emit_cmp_and_jump_insns (op1, minv, NE, NULL_RTX, mode, false,
|
||||
done_label, PROB_VERY_LIKELY);
|
||||
}
|
||||
|
||||
emit_label (do_error);
|
||||
/* Expand the ubsan builtin call. */
|
||||
expand_normal (fn);
|
||||
do_pending_stack_adjust ();
|
||||
|
||||
/* We're done. */
|
||||
emit_label (done_label);
|
||||
|
||||
if (lhs)
|
||||
emit_move_insn (target, res);
|
||||
}
|
||||
|
||||
/* Add mul overflow checking to the statement STMT. */
|
||||
|
||||
void
|
||||
ubsan_expand_si_overflow_mul_check (gimple stmt)
|
||||
{
|
||||
rtx res, op0, op1;
|
||||
tree lhs, fn, arg0, arg1;
|
||||
rtx done_label, do_error, target = NULL_RTX;
|
||||
|
||||
lhs = gimple_call_lhs (stmt);
|
||||
arg0 = gimple_call_arg (stmt, 0);
|
||||
arg1 = gimple_call_arg (stmt, 1);
|
||||
done_label = gen_label_rtx ();
|
||||
do_error = gen_label_rtx ();
|
||||
fn = ubsan_build_overflow_builtin (MULT_EXPR, gimple_location (stmt),
|
||||
TREE_TYPE (arg0), arg0, arg1);
|
||||
|
||||
do_pending_stack_adjust ();
|
||||
op0 = expand_normal (arg0);
|
||||
op1 = expand_normal (arg1);
|
||||
|
||||
enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg0));
|
||||
if (lhs)
|
||||
target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
|
||||
|
||||
enum insn_code icode = optab_handler (mulv4_optab, mode);
|
||||
if (icode != CODE_FOR_nothing)
|
||||
{
|
||||
struct expand_operand ops[4];
|
||||
rtx last = get_last_insn ();
|
||||
|
||||
res = gen_reg_rtx (mode);
|
||||
create_output_operand (&ops[0], res, mode);
|
||||
create_input_operand (&ops[1], op0, mode);
|
||||
create_input_operand (&ops[2], op1, mode);
|
||||
create_fixed_operand (&ops[3], do_error);
|
||||
if (maybe_expand_insn (icode, 4, ops))
|
||||
{
|
||||
last = get_last_insn ();
|
||||
if (profile_status != PROFILE_ABSENT
|
||||
&& JUMP_P (last)
|
||||
&& any_condjump_p (last)
|
||||
&& !find_reg_note (last, REG_BR_PROB, 0))
|
||||
add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY);
|
||||
emit_jump (done_label);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_insns_since (last);
|
||||
icode = CODE_FOR_nothing;
|
||||
}
|
||||
}
|
||||
|
||||
if (icode == CODE_FOR_nothing)
|
||||
{
|
||||
struct separate_ops ops;
|
||||
ops.op0 = arg0;
|
||||
ops.op1 = arg1;
|
||||
ops.op2 = NULL_TREE;
|
||||
ops.location = gimple_location (stmt);
|
||||
if (GET_MODE_2XWIDER_MODE (mode) != VOIDmode
|
||||
&& targetm.scalar_mode_supported_p (GET_MODE_2XWIDER_MODE (mode)))
|
||||
{
|
||||
enum machine_mode wmode = GET_MODE_2XWIDER_MODE (mode);
|
||||
ops.code = WIDEN_MULT_EXPR;
|
||||
ops.type
|
||||
= build_nonstandard_integer_type (GET_MODE_PRECISION (wmode), 0);
|
||||
|
||||
res = expand_expr_real_2 (&ops, NULL_RTX, wmode, EXPAND_NORMAL);
|
||||
rtx hipart = expand_shift (RSHIFT_EXPR, wmode, res,
|
||||
GET_MODE_PRECISION (mode), NULL_RTX, 0);
|
||||
hipart = gen_lowpart (mode, hipart);
|
||||
res = gen_lowpart (mode, res);
|
||||
rtx signbit = expand_shift (RSHIFT_EXPR, mode, res,
|
||||
GET_MODE_PRECISION (mode) - 1,
|
||||
NULL_RTX, 0);
|
||||
/* RES is low half of the double width result, HIPART
|
||||
the high half. There was overflow if
|
||||
HIPART is different from RES < 0 ? -1 : 0. */
|
||||
emit_cmp_and_jump_insns (signbit, hipart, EQ, NULL_RTX, mode,
|
||||
false, done_label, PROB_VERY_LIKELY);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For now we don't instrument this. See __mulvDI3 in libgcc2.c
|
||||
for what could be done. */
|
||||
ops.code = MULT_EXPR;
|
||||
ops.type = TREE_TYPE (arg0);
|
||||
res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
|
||||
emit_jump (done_label);
|
||||
}
|
||||
}
|
||||
|
||||
emit_label (do_error);
|
||||
/* Expand the ubsan builtin call. */
|
||||
expand_normal (fn);
|
||||
do_pending_stack_adjust ();
|
||||
|
||||
/* We're done. */
|
||||
emit_label (done_label);
|
||||
|
||||
if (lhs)
|
||||
emit_move_insn (target, res);
|
||||
}
|
||||
|
||||
/* Expand UBSAN_CHECK_ADD call STMT. */
|
||||
|
||||
static void
|
||||
expand_UBSAN_CHECK_ADD (gimple stmt)
|
||||
{
|
||||
ubsan_expand_si_overflow_addsub_check (PLUS_EXPR, stmt);
|
||||
}
|
||||
|
||||
/* Expand UBSAN_CHECK_SUB call STMT. */
|
||||
|
||||
static void
|
||||
expand_UBSAN_CHECK_SUB (gimple stmt)
|
||||
{
|
||||
if (integer_zerop (gimple_call_arg (stmt, 0)))
|
||||
ubsan_expand_si_overflow_neg_check (stmt);
|
||||
else
|
||||
ubsan_expand_si_overflow_addsub_check (MINUS_EXPR, stmt);
|
||||
}
|
||||
|
||||
/* Expand UBSAN_CHECK_MUL call STMT. */
|
||||
|
||||
static void
|
||||
expand_UBSAN_CHECK_MUL (gimple stmt)
|
||||
{
|
||||
ubsan_expand_si_overflow_mul_check (stmt);
|
||||
}
|
||||
|
||||
/* Routines to expand each internal function, indexed by function number.
|
||||
Each routine has the prototype:
|
||||
|
||||
|
@ -45,3 +45,6 @@ DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
|
||||
|
@ -187,6 +187,10 @@ OPTAB_D (movcc_optab, "mov$acc")
|
||||
OPTAB_D (cmov_optab, "cmov$a6")
|
||||
OPTAB_D (cstore_optab, "cstore$a4")
|
||||
OPTAB_D (ctrap_optab, "ctrap$a4")
|
||||
OPTAB_D (addv4_optab, "addv$I$a4")
|
||||
OPTAB_D (subv4_optab, "subv$I$a4")
|
||||
OPTAB_D (mulv4_optab, "mulv$I$a4")
|
||||
OPTAB_D (negv3_optab, "negv$I$a3")
|
||||
|
||||
OPTAB_D (smul_highpart_optab, "smul$a3_highpart")
|
||||
OPTAB_D (umul_highpart_optab, "umul$a3_highpart")
|
||||
|
@ -1460,6 +1460,8 @@ common_handle_option (struct gcc_options *opts,
|
||||
{ "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 },
|
||||
{ "return", SANITIZE_RETURN, sizeof "return" - 1 },
|
||||
{ "null", SANITIZE_NULL, sizeof "null" - 1 },
|
||||
{ "signed-integer-overflow", SANITIZE_SI_OVERFLOW,
|
||||
sizeof "signed-integer-overflow" -1 },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
const char *comma;
|
||||
|
@ -74,14 +74,6 @@ along with GCC; see the file COPYING3. If not see
|
||||
static sreal real_zero, real_one, real_almost_one, real_br_prob_base,
|
||||
real_inv_br_prob_base, real_one_half, real_bb_freq_max;
|
||||
|
||||
/* Random guesstimation given names.
|
||||
PROV_VERY_UNLIKELY should be small enough so basic block predicted
|
||||
by it gets below HOT_BB_FREQUENCY_FRACTION. */
|
||||
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
|
||||
#define PROB_EVEN (REG_BR_PROB_BASE / 2)
|
||||
#define PROB_VERY_LIKELY (REG_BR_PROB_BASE - PROB_VERY_UNLIKELY)
|
||||
#define PROB_ALWAYS (REG_BR_PROB_BASE)
|
||||
|
||||
static void combine_predictions_for_insn (rtx, basic_block);
|
||||
static void dump_prediction (FILE *, enum br_predictor, int, basic_block, int);
|
||||
static void predict_paths_leading_to (basic_block, enum br_predictor, enum prediction);
|
||||
|
@ -20,6 +20,16 @@ along with GCC; see the file COPYING3. If not see
|
||||
#ifndef GCC_PREDICT_H
|
||||
#define GCC_PREDICT_H
|
||||
|
||||
/* Random guesstimation given names.
|
||||
PROB_VERY_UNLIKELY should be small enough so basic block predicted
|
||||
by it gets below HOT_BB_FREQUENCY_FRACTION. */
|
||||
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
|
||||
#define PROB_EVEN (REG_BR_PROB_BASE / 2)
|
||||
#define PROB_VERY_LIKELY (REG_BR_PROB_BASE - PROB_VERY_UNLIKELY)
|
||||
#define PROB_ALWAYS (REG_BR_PROB_BASE)
|
||||
#define PROB_UNLIKELY (REG_BR_PROB_BASE / 5 - 1)
|
||||
#define PROB_LIKELY (PROB_ALWAYS - PROB_VERY_LIKELY)
|
||||
|
||||
#define DEF_PREDICTOR(ENUM, NAME, HITRATE, FLAGS) ENUM,
|
||||
enum br_predictor
|
||||
{
|
||||
|
@ -315,3 +315,19 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH,
|
||||
"__ubsan_handle_type_mismatch",
|
||||
BT_FN_VOID_PTR_PTR,
|
||||
ATTR_COLD_NOTHROW_LEAF_LIST)
|
||||
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW,
|
||||
"__ubsan_handle_add_overflow",
|
||||
BT_FN_VOID_PTR_PTR_PTR,
|
||||
ATTR_COLD_NOTHROW_LEAF_LIST)
|
||||
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW,
|
||||
"__ubsan_handle_sub_overflow",
|
||||
BT_FN_VOID_PTR_PTR_PTR,
|
||||
ATTR_COLD_NOTHROW_LEAF_LIST)
|
||||
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW,
|
||||
"__ubsan_handle_mul_overflow",
|
||||
BT_FN_VOID_PTR_PTR_PTR,
|
||||
ATTR_COLD_NOTHROW_LEAF_LIST)
|
||||
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW,
|
||||
"__ubsan_handle_negate_overflow",
|
||||
BT_FN_VOID_PTR_PTR,
|
||||
ATTR_COLD_NOTHROW_LEAF_LIST)
|
||||
|
@ -1,3 +1,13 @@
|
||||
2013-12-04 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* c-c++-common/ubsan/overflow-mul-2.c: New test.
|
||||
* c-c++-common/ubsan/overflow-add-1.c: New test.
|
||||
* c-c++-common/ubsan/overflow-add-2.c: New test.
|
||||
* c-c++-common/ubsan/overflow-mul-1.c: New test.
|
||||
* c-c++-common/ubsan/overflow-sub-1.c: New test.
|
||||
* c-c++-common/ubsan/overflow-sub-2.c: New test.
|
||||
* c-c++-common/ubsan/overflow-negate-1.c: New test.
|
||||
|
||||
2013-12-04 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
PR c/54113
|
||||
|
61
gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c
Normal file
61
gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c
Normal file
@ -0,0 +1,61 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define SCHAR_MAX __SCHAR_MAX__
|
||||
#define SHRT_MAX __SHRT_MAX__
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
|
||||
void __attribute__((noinline,noclone))
|
||||
check (int i, int j)
|
||||
{
|
||||
if (i != j)
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
#if __INT_MAX__ == 2147483647
|
||||
/* Here, nothing should fail. */
|
||||
volatile int j = INT_MAX;
|
||||
volatile int i = -1;
|
||||
volatile int k = j + i;
|
||||
check (k, 2147483646);
|
||||
k = i + j;
|
||||
check (k, 2147483646);
|
||||
j--;
|
||||
check (j, 2147483646);
|
||||
|
||||
i = 1;
|
||||
j = INT_MIN;
|
||||
k = i + j;
|
||||
check (k, -2147483647);
|
||||
k = j + i;
|
||||
check (k, -2147483647);
|
||||
j++;
|
||||
check (j, -2147483647);
|
||||
#endif
|
||||
|
||||
/* Test integer promotion. */
|
||||
#if __SCHAR_MAX__ == 127
|
||||
volatile signed char a = SCHAR_MAX;
|
||||
volatile signed char b = 1;
|
||||
volatile signed char c = a + b;
|
||||
check (c, -128);
|
||||
a++;
|
||||
check (a, -128);
|
||||
#endif
|
||||
|
||||
#if __SHRT_MAX__ == 32767
|
||||
volatile short d = SHRT_MAX;
|
||||
volatile short e = 1;
|
||||
volatile short f = d + e;
|
||||
check (f, -32768);
|
||||
d++;
|
||||
check (d, -32768);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
61
gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c
Normal file
61
gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c
Normal file
@ -0,0 +1,61 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
#define LONG_MAX __LONG_MAX__
|
||||
#define LONG_MIN (-__LONG_MAX__ - 1L)
|
||||
#define LLONG_MAX __LONG_LONG_MAX__
|
||||
#define LLONG_MIN (-__LONG_LONG_MAX__ - 1L)
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
volatile int j = INT_MAX;
|
||||
volatile int i = 1;
|
||||
volatile int k = j + i;
|
||||
k = i + j;
|
||||
j++;
|
||||
j = INT_MAX - 100;
|
||||
j += (1 << 10);
|
||||
|
||||
j = INT_MIN;
|
||||
i = -1;
|
||||
k = i + j;
|
||||
k = j + i;
|
||||
j = INT_MIN + 100;
|
||||
j += -(1 << 10);
|
||||
|
||||
volatile long int m = LONG_MAX;
|
||||
volatile long int n = 1;
|
||||
volatile long int o = m + n;
|
||||
o = n + m;
|
||||
m++;
|
||||
m = LONG_MAX - 100;
|
||||
m += (1 << 10);
|
||||
|
||||
m = LONG_MIN;
|
||||
n = -1;
|
||||
o = m + n;
|
||||
o = n + m;
|
||||
m = LONG_MIN + 100;
|
||||
m += -(1 << 10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 2147483547 \\+ 1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -2147483648 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ \[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -\[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
47
gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c
Normal file
47
gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c
Normal file
@ -0,0 +1,47 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define SCHAR_MAX __SCHAR_MAX__
|
||||
#define SHRT_MAX __SHRT_MAX__
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
|
||||
void __attribute__((noinline,noclone))
|
||||
check (int i, int j)
|
||||
{
|
||||
if (i != j)
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* Test integer promotion. */
|
||||
#if __SCHAR_MAX__ == 127
|
||||
volatile signed char a = -2;
|
||||
volatile signed char b = SCHAR_MAX;
|
||||
volatile signed char c = a * b;
|
||||
check (c, 2);
|
||||
#endif
|
||||
|
||||
#if __SHRT_MAX__ == 32767
|
||||
volatile short d = SHRT_MAX;
|
||||
volatile short e = 2;
|
||||
volatile short f = d * e;
|
||||
check (f, -2);
|
||||
#endif
|
||||
|
||||
#if __INT_MAX__ == 2147483647
|
||||
volatile int m = INT_MAX;
|
||||
volatile int n = 1;
|
||||
volatile int o = m * n;
|
||||
check (o, INT_MAX);
|
||||
|
||||
m = INT_MIN;
|
||||
o = m * n;
|
||||
check (o, INT_MIN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
27
gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c
Normal file
27
gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c
Normal file
@ -0,0 +1,27 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define LONG_MAX __LONG_MAX__
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
volatile int j = INT_MAX;
|
||||
volatile int i = 2;
|
||||
volatile int k = j * i;
|
||||
k = i * j;
|
||||
|
||||
volatile long int m = LONG_MAX;
|
||||
volatile long int n = 2;
|
||||
volatile long int o = m * n;
|
||||
o = n * m;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-output "signed integer overflow: 2147483647 \\* 2 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\* 2 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* \[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
14
gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c
Normal file
14
gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c
Normal file
@ -0,0 +1,14 @@
|
||||
/* { dg-do run { target int128 } } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
int j = INT_MIN;
|
||||
return -j;
|
||||
}
|
||||
|
||||
/* { dg-output "negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
|
63
gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c
Normal file
63
gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c
Normal file
@ -0,0 +1,63 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define SCHAR_MAX __SCHAR_MAX__
|
||||
#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
|
||||
#define SHRT_MAX __SHRT_MAX__
|
||||
#define SHRT_MIN (-__SHRT_MAX__ - 1)
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
|
||||
void __attribute__((noinline,noclone))
|
||||
check (int i, int j)
|
||||
{
|
||||
if (i != j)
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
#if __INT_MAX__ == 2147483647
|
||||
/* Here, nothing should fail. */
|
||||
volatile int i = -1;
|
||||
volatile int j = INT_MIN;
|
||||
volatile int k = j - i;
|
||||
check (k, -2147483647);
|
||||
k = i - j;
|
||||
check (k, 2147483647);
|
||||
j++;
|
||||
check (j, -2147483647);
|
||||
|
||||
i = 1;
|
||||
j = INT_MAX;
|
||||
k = i - j;
|
||||
check (k, -2147483646);
|
||||
k = j - i;
|
||||
check (k, 2147483646);
|
||||
j--;
|
||||
check (k, 2147483646);
|
||||
#endif
|
||||
|
||||
/* Test integer promotion. */
|
||||
#if __SCHAR_MAX__ == 127
|
||||
volatile signed char a = SCHAR_MIN;
|
||||
volatile signed char b = 1;
|
||||
volatile signed char c = a - b;
|
||||
check (c, 127);
|
||||
a--;
|
||||
check (a, 127);
|
||||
#endif
|
||||
|
||||
#if __SHRT_MAX__ == 32767
|
||||
volatile short d = SHRT_MIN;
|
||||
volatile short e = 1;
|
||||
volatile short f = d - e;
|
||||
check (f, 32767);
|
||||
d--;
|
||||
check (d, 32767);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
55
gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c
Normal file
55
gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
|
||||
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
|
||||
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
#define LONG_MAX __LONG_MAX__
|
||||
#define LONG_MIN (-__LONG_MAX__ - 1L)
|
||||
#define LLONG_MAX __LONG_LONG_MAX__
|
||||
#define LLONG_MIN (-__LONG_LONG_MAX__ - 1L)
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
volatile int j = INT_MIN;
|
||||
volatile int i = 1;
|
||||
volatile int k = j - i;
|
||||
j--;
|
||||
j = INT_MIN + 100;
|
||||
j -= (1 << 10);
|
||||
|
||||
j = INT_MIN;
|
||||
i = -1;
|
||||
k = j - -i;
|
||||
|
||||
i = INT_MIN + 1000;
|
||||
i -= (1 << 20);
|
||||
|
||||
volatile long int l = LONG_MIN;
|
||||
volatile long int m = 1;
|
||||
volatile long int n = l - m;
|
||||
l--;
|
||||
l = LONG_MIN + 100;
|
||||
l -= (1 << 10);
|
||||
|
||||
l = LONG_MIN;
|
||||
m = -1;
|
||||
n = l - -m;
|
||||
|
||||
m = LONG_MIN + 1000;
|
||||
m -= (1 << 20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-output "signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -2147482648 \\+ -1048576 cannot be represented in type 'int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
||||
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1048576 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
|
@ -55,14 +55,9 @@
|
||||
#include "gimple-pretty-print.h"
|
||||
#include "cfgloop.h"
|
||||
#include "tree-ssa-address.h"
|
||||
#include "predict.h"
|
||||
|
||||
|
||||
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
|
||||
#define PROB_VERY_LIKELY (PROB_ALWAYS - PROB_VERY_UNLIKELY)
|
||||
#define PROB_UNLIKELY (REG_BR_PROB_BASE / 5 - 1)
|
||||
#define PROB_LIKELY (PROB_ALWAYS - PROB_VERY_LIKELY)
|
||||
#define PROB_ALWAYS (REG_BR_PROB_BASE)
|
||||
|
||||
#define A_RUNINSTRUMENTEDCODE 0x0001
|
||||
#define A_RUNUNINSTRUMENTEDCODE 0x0002
|
||||
#define A_SAVELIVEVARIABLES 0x0004
|
||||
|
@ -3757,6 +3757,47 @@ extract_range_basic (value_range_t *vr, gimple stmt)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (is_gimple_call (stmt)
|
||||
&& gimple_call_internal_p (stmt))
|
||||
{
|
||||
enum tree_code subcode = ERROR_MARK;
|
||||
switch (gimple_call_internal_fn (stmt))
|
||||
{
|
||||
case IFN_UBSAN_CHECK_ADD:
|
||||
subcode = PLUS_EXPR;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_SUB:
|
||||
subcode = MINUS_EXPR;
|
||||
break;
|
||||
case IFN_UBSAN_CHECK_MUL:
|
||||
subcode = MULT_EXPR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (subcode != ERROR_MARK)
|
||||
{
|
||||
bool saved_flag_wrapv = flag_wrapv;
|
||||
/* Pretend the arithmetics is wrapping. If there is
|
||||
any overflow, we'll complain, but will actually do
|
||||
wrapping operation. */
|
||||
flag_wrapv = 1;
|
||||
extract_range_from_binary_expr (vr, subcode, type,
|
||||
gimple_call_arg (stmt, 0),
|
||||
gimple_call_arg (stmt, 1));
|
||||
flag_wrapv = saved_flag_wrapv;
|
||||
|
||||
/* If for both arguments vrp_valueize returned non-NULL,
|
||||
this should have been already folded and if not, it
|
||||
wasn't folded because of overflow. Avoid removing the
|
||||
UBSAN_CHECK_* calls in that case. */
|
||||
if (vr->type == VR_RANGE
|
||||
&& (vr->min == vr->max
|
||||
|| operand_equal_p (vr->min, vr->max, 0)))
|
||||
set_value_range_to_varying (vr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (INTEGRAL_TYPE_P (type)
|
||||
&& gimple_stmt_nonnegative_warnv_p (stmt, &sop))
|
||||
set_value_range_to_nonnegative (vr, type,
|
||||
|
101
gcc/ubsan.c
101
gcc/ubsan.c
@ -41,9 +41,6 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "ubsan.h"
|
||||
#include "c-family/c-common.h"
|
||||
|
||||
/* From trans-mem.c. */
|
||||
#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1)
|
||||
|
||||
/* Map from a tree to a VAR_DECL tree. */
|
||||
|
||||
struct GTY(()) tree_type_map {
|
||||
@ -632,6 +629,98 @@ instrument_null (gimple_stmt_iterator gsi, bool is_lhs)
|
||||
instrument_member_call (&gsi);
|
||||
}
|
||||
|
||||
/* Build an ubsan builtin call for the signed-integer-overflow
|
||||
sanitization. CODE says what kind of builtin are we building,
|
||||
LOC is a location, LHSTYPE is the type of LHS, OP0 and OP1
|
||||
are operands of the binary operation. */
|
||||
|
||||
tree
|
||||
ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
|
||||
tree op0, tree op1)
|
||||
{
|
||||
tree data = ubsan_create_data ("__ubsan_overflow_data", loc, NULL,
|
||||
ubsan_type_descriptor (lhstype, false),
|
||||
NULL_TREE);
|
||||
enum built_in_function fn_code;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
fn_code = BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW;
|
||||
break;
|
||||
case MINUS_EXPR:
|
||||
fn_code = BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW;
|
||||
break;
|
||||
case MULT_EXPR:
|
||||
fn_code = BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW;
|
||||
break;
|
||||
case NEGATE_EXPR:
|
||||
fn_code = BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW;
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
tree fn = builtin_decl_explicit (fn_code);
|
||||
return build_call_expr_loc (loc, fn, 2 + (code != NEGATE_EXPR),
|
||||
build_fold_addr_expr_loc (loc, data),
|
||||
ubsan_encode_value (op0),
|
||||
op1 ? ubsan_encode_value (op1) : NULL_TREE);
|
||||
}
|
||||
|
||||
/* Perform the signed integer instrumentation. GSI is the iterator
|
||||
pointing at statement we are trying to instrument. */
|
||||
|
||||
static void
|
||||
instrument_si_overflow (gimple_stmt_iterator gsi)
|
||||
{
|
||||
gimple stmt = gsi_stmt (gsi);
|
||||
tree_code code = gimple_assign_rhs_code (stmt);
|
||||
tree lhs = gimple_assign_lhs (stmt);
|
||||
tree lhstype = TREE_TYPE (lhs);
|
||||
tree a, b;
|
||||
gimple g;
|
||||
|
||||
/* If this is not a signed operation, don't instrument anything here.
|
||||
Also punt on bit-fields. */
|
||||
if (!INTEGRAL_TYPE_P (lhstype)
|
||||
|| TYPE_OVERFLOW_WRAPS (lhstype)
|
||||
|| GET_MODE_BITSIZE (TYPE_MODE (lhstype)) != TYPE_PRECISION (lhstype))
|
||||
return;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case MINUS_EXPR:
|
||||
case PLUS_EXPR:
|
||||
case MULT_EXPR:
|
||||
/* Transform
|
||||
i = u {+,-,*} 5;
|
||||
into
|
||||
i = UBSAN_CHECK_{ADD,SUB,MUL} (u, 5); */
|
||||
a = gimple_assign_rhs1 (stmt);
|
||||
b = gimple_assign_rhs2 (stmt);
|
||||
g = gimple_build_call_internal (code == PLUS_EXPR
|
||||
? IFN_UBSAN_CHECK_ADD
|
||||
: code == MINUS_EXPR
|
||||
? IFN_UBSAN_CHECK_SUB
|
||||
: IFN_UBSAN_CHECK_MUL, 2, a, b);
|
||||
gimple_call_set_lhs (g, lhs);
|
||||
gsi_replace (&gsi, g, false);
|
||||
break;
|
||||
case NEGATE_EXPR:
|
||||
/* Represent i = -u;
|
||||
as
|
||||
i = UBSAN_CHECK_SUB (0, u); */
|
||||
a = build_int_cst (lhstype, 0);
|
||||
b = gimple_assign_rhs1 (stmt);
|
||||
g = gimple_build_call_internal (IFN_UBSAN_CHECK_SUB, 2, a, b);
|
||||
gimple_call_set_lhs (g, lhs);
|
||||
gsi_replace (&gsi, g, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gate and execute functions for ubsan pass. */
|
||||
|
||||
static unsigned int
|
||||
@ -651,6 +740,10 @@ ubsan_pass (void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flag_sanitize & SANITIZE_SI_OVERFLOW)
|
||||
&& is_gimple_assign (stmt))
|
||||
instrument_si_overflow (gsi);
|
||||
|
||||
if (flag_sanitize & SANITIZE_NULL)
|
||||
{
|
||||
if (gimple_store_p (stmt))
|
||||
@ -668,7 +761,7 @@ ubsan_pass (void)
|
||||
static bool
|
||||
gate_ubsan (void)
|
||||
{
|
||||
return flag_sanitize & SANITIZE_NULL;
|
||||
return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -43,6 +43,7 @@ extern tree ubsan_create_data (const char *, location_t,
|
||||
extern tree ubsan_type_descriptor (tree, bool);
|
||||
extern tree ubsan_encode_value (tree);
|
||||
extern bool is_ubsan_builtin_p (tree);
|
||||
extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
|
||||
|
||||
#endif /* GCC_UBSAN_H */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user