regrename.h (struct du_head): Add target_data_1 and target_data_2 fields.
* regrename.h (struct du_head): Add target_data_1 and target_data_2 fields. * regrename.c (create_new_chain): Clear entire struct after allocating. * config/i386/i386.opt (mmitigate-rop): New option. * doc/invoke.texi (mmitigate-rop): Document. * config/i386/i386.c: Include "regrename.h". (ix86_rop_should_change_byte_p, reg_encoded_number, ix86_get_modrm_for_rop, set_rop_modrm_reg_bits, ix86_mitigate_rop): New static functions. (ix86_reorg): Call ix86_mitigate_rop if -fmitigate-rop. * config/i386/i386.md (attr "modrm_class"): New. (cmp<mode>_ccno_1, mov<mode>_xor, movstrict<mode>_xor, x86_mov<mode>cc_0_m1. x86_mov<mode>cc_0_m1_se) (x86_mov<mode>cc_0_m1_neg): Override modrm_class attribute. From-SVN: r230543
This commit is contained in:
parent
98f84050d6
commit
d085c46817
7 changed files with 371 additions and 5 deletions
|
@ -1,3 +1,21 @@
|
|||
2015-11-18 Bernd Schmidt <bschmidt@redhat.com>
|
||||
|
||||
* regrename.h (struct du_head): Add target_data_1 and target_data_2
|
||||
fields.
|
||||
* regrename.c (create_new_chain): Clear entire struct after allocating.
|
||||
|
||||
* config/i386/i386.opt (mmitigate-rop): New option.
|
||||
* doc/invoke.texi (mmitigate-rop): Document.
|
||||
* config/i386/i386.c: Include "regrename.h".
|
||||
(ix86_rop_should_change_byte_p, reg_encoded_number,
|
||||
ix86_get_modrm_for_rop, set_rop_modrm_reg_bits, ix86_mitigate_rop): New
|
||||
static functions.
|
||||
(ix86_reorg): Call ix86_mitigate_rop if -fmitigate-rop.
|
||||
* config/i386/i386.md (attr "modrm_class"): New.
|
||||
(cmp<mode>_ccno_1, mov<mode>_xor, movstrict<mode>_xor,
|
||||
x86_mov<mode>cc_0_m1. x86_mov<mode>cc_0_m1_se)
|
||||
(x86_mov<mode>cc_0_m1_neg): Override modrm_class attribute.
|
||||
|
||||
2015-11-18 Ilya Enkovich <enkovich.gnu@gmail.com>
|
||||
|
||||
PR target/68405
|
||||
|
|
|
@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "rtl-chkp.h"
|
||||
#include "dbgcnt.h"
|
||||
#include "case-cfn-macros.h"
|
||||
#include "regrename.h"
|
||||
|
||||
/* This file should be included last. */
|
||||
#include "target-def.h"
|
||||
|
@ -3974,6 +3975,15 @@ ix86_debug_options (void)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Return true if T is one of the bytes we should avoid with
|
||||
-fmitigate-rop. */
|
||||
|
||||
static bool
|
||||
ix86_rop_should_change_byte_p (int t)
|
||||
{
|
||||
return t == 0xc2 || t == 0xc3 || t == 0xca || t == 0xcb;
|
||||
}
|
||||
|
||||
static const char *stringop_alg_names[] = {
|
||||
#define DEF_ENUM
|
||||
#define DEF_ALG(alg, name) #name,
|
||||
|
@ -27303,6 +27313,100 @@ ix86_instantiate_decls (void)
|
|||
instantiate_decl_rtl (s->rtl);
|
||||
}
|
||||
|
||||
/* Return the number used for encoding REG, in the range 0..7. */
|
||||
|
||||
static int
|
||||
reg_encoded_number (rtx reg)
|
||||
{
|
||||
unsigned regno = REGNO (reg);
|
||||
switch (regno)
|
||||
{
|
||||
case AX_REG:
|
||||
return 0;
|
||||
case CX_REG:
|
||||
return 1;
|
||||
case DX_REG:
|
||||
return 2;
|
||||
case BX_REG:
|
||||
return 3;
|
||||
case SP_REG:
|
||||
return 4;
|
||||
case BP_REG:
|
||||
return 5;
|
||||
case SI_REG:
|
||||
return 6;
|
||||
case DI_REG:
|
||||
return 7;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG))
|
||||
return regno - FIRST_STACK_REG;
|
||||
if (IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG))
|
||||
return regno - FIRST_SSE_REG;
|
||||
if (IN_RANGE (regno, FIRST_MMX_REG, LAST_MMX_REG))
|
||||
return regno - FIRST_MMX_REG;
|
||||
if (IN_RANGE (regno, FIRST_REX_SSE_REG, LAST_REX_SSE_REG))
|
||||
return regno - FIRST_REX_SSE_REG;
|
||||
if (IN_RANGE (regno, FIRST_REX_INT_REG, LAST_REX_INT_REG))
|
||||
return regno - FIRST_REX_INT_REG;
|
||||
if (IN_RANGE (regno, FIRST_MASK_REG, LAST_MASK_REG))
|
||||
return regno - FIRST_MASK_REG;
|
||||
if (IN_RANGE (regno, FIRST_BND_REG, LAST_BND_REG))
|
||||
return regno - FIRST_BND_REG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Given an insn INSN with NOPERANDS OPERANDS, return the modr/m byte used
|
||||
in its encoding if it could be relevant for ROP mitigation, otherwise
|
||||
return -1. If POPNO0 and POPNO1 are nonnull, store the operand numbers
|
||||
used for calculating it into them. */
|
||||
|
||||
static int
|
||||
ix86_get_modrm_for_rop (rtx_insn *insn, rtx *operands, int noperands,
|
||||
int *popno0 = 0, int *popno1 = 0)
|
||||
{
|
||||
if (asm_noperands (PATTERN (insn)) >= 0)
|
||||
return -1;
|
||||
int has_modrm = get_attr_modrm (insn);
|
||||
if (!has_modrm)
|
||||
return -1;
|
||||
enum attr_modrm_class cls = get_attr_modrm_class (insn);
|
||||
rtx op0, op1;
|
||||
switch (cls)
|
||||
{
|
||||
case MODRM_CLASS_OP02:
|
||||
gcc_assert (noperands >= 3);
|
||||
if (popno0)
|
||||
{
|
||||
*popno0 = 0;
|
||||
*popno1 = 2;
|
||||
}
|
||||
op0 = operands[0];
|
||||
op1 = operands[2];
|
||||
break;
|
||||
case MODRM_CLASS_OP01:
|
||||
gcc_assert (noperands >= 2);
|
||||
if (popno0)
|
||||
{
|
||||
*popno0 = 0;
|
||||
*popno1 = 1;
|
||||
}
|
||||
op0 = operands[0];
|
||||
op1 = operands[1];
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
if (REG_P (op0) && REG_P (op1))
|
||||
{
|
||||
int enc0 = reg_encoded_number (op0);
|
||||
int enc1 = reg_encoded_number (op1);
|
||||
return 0xc0 + (enc1 << 3) + enc0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check whether x86 address PARTS is a pc-relative address. */
|
||||
|
||||
static bool
|
||||
|
@ -45098,6 +45202,215 @@ ix86_seh_fixup_eh_fallthru (void)
|
|||
}
|
||||
}
|
||||
|
||||
/* Given a register number BASE, the lowest of a group of registers, update
|
||||
regsets IN and OUT with the registers that should be avoided in input
|
||||
and output operands respectively when trying to avoid generating a modr/m
|
||||
byte for -fmitigate-rop. */
|
||||
|
||||
static void
|
||||
set_rop_modrm_reg_bits (int base, HARD_REG_SET &in, HARD_REG_SET &out)
|
||||
{
|
||||
SET_HARD_REG_BIT (out, base);
|
||||
SET_HARD_REG_BIT (out, base + 1);
|
||||
SET_HARD_REG_BIT (in, base + 2);
|
||||
SET_HARD_REG_BIT (in, base + 3);
|
||||
}
|
||||
|
||||
/* Called if -fmitigate_rop is in effect. Try to rewrite instructions so
|
||||
that certain encodings of modr/m bytes do not occur. */
|
||||
static void
|
||||
ix86_mitigate_rop (void)
|
||||
{
|
||||
HARD_REG_SET input_risky;
|
||||
HARD_REG_SET output_risky;
|
||||
HARD_REG_SET inout_risky;
|
||||
|
||||
CLEAR_HARD_REG_SET (output_risky);
|
||||
CLEAR_HARD_REG_SET (input_risky);
|
||||
SET_HARD_REG_BIT (output_risky, AX_REG);
|
||||
SET_HARD_REG_BIT (output_risky, CX_REG);
|
||||
SET_HARD_REG_BIT (input_risky, BX_REG);
|
||||
SET_HARD_REG_BIT (input_risky, DX_REG);
|
||||
set_rop_modrm_reg_bits (FIRST_SSE_REG, input_risky, output_risky);
|
||||
set_rop_modrm_reg_bits (FIRST_REX_INT_REG, input_risky, output_risky);
|
||||
set_rop_modrm_reg_bits (FIRST_REX_SSE_REG, input_risky, output_risky);
|
||||
set_rop_modrm_reg_bits (FIRST_EXT_REX_SSE_REG, input_risky, output_risky);
|
||||
set_rop_modrm_reg_bits (FIRST_MASK_REG, input_risky, output_risky);
|
||||
set_rop_modrm_reg_bits (FIRST_BND_REG, input_risky, output_risky);
|
||||
COPY_HARD_REG_SET (inout_risky, input_risky);
|
||||
IOR_HARD_REG_SET (inout_risky, output_risky);
|
||||
|
||||
compute_bb_for_insn ();
|
||||
df_note_add_problem ();
|
||||
df_analyze ();
|
||||
|
||||
regrename_init (true);
|
||||
regrename_analyze (NULL);
|
||||
|
||||
auto_vec<du_head_p> cands;
|
||||
|
||||
for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
||||
{
|
||||
if (!NONDEBUG_INSN_P (insn))
|
||||
continue;
|
||||
|
||||
if (GET_CODE (PATTERN (insn)) == USE
|
||||
|| GET_CODE (PATTERN (insn)) == CLOBBER)
|
||||
continue;
|
||||
|
||||
extract_insn (insn);
|
||||
|
||||
int opno0, opno1;
|
||||
int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand,
|
||||
recog_data.n_operands, &opno0,
|
||||
&opno1);
|
||||
|
||||
if (!ix86_rop_should_change_byte_p (modrm))
|
||||
continue;
|
||||
|
||||
insn_rr_info *info = &insn_rr[INSN_UID (insn)];
|
||||
|
||||
/* This happens when regrename has to fail a block. */
|
||||
if (!info->op_info)
|
||||
continue;
|
||||
|
||||
if (info->op_info[opno0].n_chains != 0)
|
||||
{
|
||||
gcc_assert (info->op_info[opno0].n_chains == 1);
|
||||
du_head_p op0c;
|
||||
op0c = regrename_chain_from_id (info->op_info[opno0].heads[0]->id);
|
||||
if (op0c->target_data_1 + op0c->target_data_2 == 0
|
||||
&& !op0c->cannot_rename)
|
||||
cands.safe_push (op0c);
|
||||
|
||||
op0c->target_data_1++;
|
||||
}
|
||||
if (info->op_info[opno1].n_chains != 0)
|
||||
{
|
||||
gcc_assert (info->op_info[opno1].n_chains == 1);
|
||||
du_head_p op1c;
|
||||
op1c = regrename_chain_from_id (info->op_info[opno1].heads[0]->id);
|
||||
if (op1c->target_data_1 + op1c->target_data_2 == 0
|
||||
&& !op1c->cannot_rename)
|
||||
cands.safe_push (op1c);
|
||||
|
||||
op1c->target_data_2++;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
du_head_p head;
|
||||
FOR_EACH_VEC_ELT (cands, i, head)
|
||||
{
|
||||
int old_reg, best_reg;
|
||||
HARD_REG_SET unavailable;
|
||||
|
||||
CLEAR_HARD_REG_SET (unavailable);
|
||||
if (head->target_data_1)
|
||||
IOR_HARD_REG_SET (unavailable, output_risky);
|
||||
if (head->target_data_2)
|
||||
IOR_HARD_REG_SET (unavailable, input_risky);
|
||||
|
||||
int n_uses;
|
||||
reg_class superclass = regrename_find_superclass (head, &n_uses,
|
||||
&unavailable);
|
||||
old_reg = head->regno;
|
||||
best_reg = find_rename_reg (head, superclass, &unavailable,
|
||||
old_reg, false);
|
||||
bool ok = regrename_do_replace (head, best_reg);
|
||||
gcc_assert (ok);
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Chain %d renamed as %s in %s\n", head->id,
|
||||
reg_names[best_reg], reg_class_names[superclass]);
|
||||
|
||||
}
|
||||
|
||||
regrename_finish ();
|
||||
|
||||
df_analyze ();
|
||||
|
||||
basic_block bb;
|
||||
regset_head live;
|
||||
|
||||
INIT_REG_SET (&live);
|
||||
|
||||
FOR_EACH_BB_FN (bb, cfun)
|
||||
{
|
||||
rtx_insn *insn;
|
||||
|
||||
COPY_REG_SET (&live, DF_LR_OUT (bb));
|
||||
df_simulate_initialize_backwards (bb, &live);
|
||||
|
||||
FOR_BB_INSNS_REVERSE (bb, insn)
|
||||
{
|
||||
if (!NONDEBUG_INSN_P (insn))
|
||||
continue;
|
||||
|
||||
df_simulate_one_insn_backwards (bb, insn, &live);
|
||||
|
||||
if (GET_CODE (PATTERN (insn)) == USE
|
||||
|| GET_CODE (PATTERN (insn)) == CLOBBER)
|
||||
continue;
|
||||
|
||||
extract_insn (insn);
|
||||
constrain_operands_cached (insn, reload_completed);
|
||||
int opno0, opno1;
|
||||
int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand,
|
||||
recog_data.n_operands, &opno0,
|
||||
&opno1);
|
||||
if (modrm < 0
|
||||
|| !ix86_rop_should_change_byte_p (modrm)
|
||||
|| opno0 == opno1)
|
||||
continue;
|
||||
|
||||
rtx oldreg = recog_data.operand[opno1];
|
||||
preprocess_constraints (insn);
|
||||
const operand_alternative *alt = which_op_alt ();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < recog_data.n_operands; i++)
|
||||
if (i != opno1
|
||||
&& alt[i].earlyclobber
|
||||
&& reg_overlap_mentioned_p (recog_data.operand[i],
|
||||
oldreg))
|
||||
break;
|
||||
|
||||
if (i < recog_data.n_operands)
|
||||
continue;
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"attempting to fix modrm byte in insn %d:"
|
||||
" reg %d class %s", INSN_UID (insn), REGNO (oldreg),
|
||||
reg_class_names[alt[opno1].cl]);
|
||||
|
||||
HARD_REG_SET unavailable;
|
||||
REG_SET_TO_HARD_REG_SET (unavailable, &live);
|
||||
SET_HARD_REG_BIT (unavailable, REGNO (oldreg));
|
||||
IOR_COMPL_HARD_REG_SET (unavailable, call_used_reg_set);
|
||||
IOR_HARD_REG_SET (unavailable, fixed_reg_set);
|
||||
IOR_HARD_REG_SET (unavailable, output_risky);
|
||||
IOR_COMPL_HARD_REG_SET (unavailable,
|
||||
reg_class_contents[alt[opno1].cl]);
|
||||
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (!TEST_HARD_REG_BIT (unavailable, i))
|
||||
break;
|
||||
if (i == FIRST_PSEUDO_REGISTER)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, ", none available\n");
|
||||
continue;
|
||||
}
|
||||
if (dump_file)
|
||||
fprintf (dump_file, " -> %d\n", i);
|
||||
rtx newreg = gen_rtx_REG (recog_data.operand_mode[opno1], i);
|
||||
validate_change (insn, recog_data.operand_loc[opno1], newreg, false);
|
||||
insn = emit_insn_before (gen_move_insn (newreg, oldreg), insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement machine specific optimizations. We implement padding of returns
|
||||
for K8 CPUs and pass to avoid 4 jumps in the single 16 byte window. */
|
||||
static void
|
||||
|
@ -45107,6 +45420,9 @@ ix86_reorg (void)
|
|||
with old MDEP_REORGS that are not CFG based. Recompute it now. */
|
||||
compute_bb_for_insn ();
|
||||
|
||||
if (flag_mitigate_rop)
|
||||
ix86_mitigate_rop ();
|
||||
|
||||
if (TARGET_SEH && current_function_has_exception_handlers ())
|
||||
ix86_seh_fixup_eh_fallthru ();
|
||||
|
||||
|
|
|
@ -637,6 +637,19 @@
|
|||
]
|
||||
(const_int 1)))
|
||||
|
||||
(define_attr "modrm_class" "none,incdec,op0,op01,op02,pushpop,unknown"
|
||||
(cond [(eq_attr "modrm" "0")
|
||||
(const_string "none")
|
||||
(eq_attr "type" "alu,imul,ishift")
|
||||
(const_string "op02")
|
||||
(eq_attr "type" "imov,imovx,lea,alu1,icmp")
|
||||
(const_string "op01")
|
||||
(eq_attr "type" "incdec")
|
||||
(const_string "incdec")
|
||||
(eq_attr "type" "push,pop")
|
||||
(const_string "pushpop")]
|
||||
(const_string "unknown")))
|
||||
|
||||
;; The (bounding maximum) length of an instruction in bytes.
|
||||
;; ??? fistp and frndint are in fact fldcw/{fistp,frndint}/fldcw sequences.
|
||||
;; Later we may want to split them and compute proper length as for
|
||||
|
@ -1235,6 +1248,7 @@
|
|||
cmp{<imodesuffix>}\t{%1, %0|%0, %1}"
|
||||
[(set_attr "type" "test,icmp")
|
||||
(set_attr "length_immediate" "0,1")
|
||||
(set_attr "modrm_class" "op0,unknown")
|
||||
(set_attr "mode" "<MODE>")])
|
||||
|
||||
(define_insn "*cmp<mode>_1"
|
||||
|
@ -1942,6 +1956,7 @@
|
|||
"reload_completed"
|
||||
"xor{l}\t%k0, %k0"
|
||||
[(set_attr "type" "alu1")
|
||||
(set_attr "modrm_class" "op0")
|
||||
(set_attr "mode" "SI")
|
||||
(set_attr "length_immediate" "0")])
|
||||
|
||||
|
@ -2713,6 +2728,7 @@
|
|||
"reload_completed"
|
||||
"xor{<imodesuffix>}\t%0, %0"
|
||||
[(set_attr "type" "alu1")
|
||||
(set_attr "modrm_class" "op0")
|
||||
(set_attr "mode" "<MODE>")
|
||||
(set_attr "length_immediate" "0")])
|
||||
|
||||
|
@ -16847,6 +16863,7 @@
|
|||
; Since we don't have the proper number of operands for an alu insn,
|
||||
; fill in all the blanks.
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "modrm_class" "op0")
|
||||
(set_attr "use_carry" "1")
|
||||
(set_attr "pent_pair" "pu")
|
||||
(set_attr "memory" "none")
|
||||
|
@ -16864,6 +16881,7 @@
|
|||
""
|
||||
"sbb{<imodesuffix>}\t%0, %0"
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "modrm_class" "op0")
|
||||
(set_attr "use_carry" "1")
|
||||
(set_attr "pent_pair" "pu")
|
||||
(set_attr "memory" "none")
|
||||
|
@ -16879,6 +16897,7 @@
|
|||
""
|
||||
"sbb{<imodesuffix>}\t%0, %0"
|
||||
[(set_attr "type" "alu")
|
||||
(set_attr "modrm_class" "op0")
|
||||
(set_attr "use_carry" "1")
|
||||
(set_attr "pent_pair" "pu")
|
||||
(set_attr "memory" "none")
|
||||
|
|
|
@ -889,3 +889,7 @@ Enum(stack_protector_guard) String(tls) Value(SSP_TLS)
|
|||
|
||||
EnumValue
|
||||
Enum(stack_protector_guard) String(global) Value(SSP_GLOBAL)
|
||||
|
||||
mmitigate-rop
|
||||
Target Var(flag_mitigate_rop) Init(0)
|
||||
Attempt to avoid generating instruction sequences containing ret bytes.
|
||||
|
|
|
@ -1116,7 +1116,8 @@ See RS/6000 and PowerPC Options.
|
|||
-m32 -m64 -mx32 -m16 -miamcu -mlarge-data-threshold=@var{num} @gol
|
||||
-msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
|
||||
-mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
|
||||
-malign-data=@var{type} -mstack-protector-guard=@var{guard}}
|
||||
-malign-data=@var{type} -mstack-protector-guard=@var{guard} @gol
|
||||
-mmitigate-rop}
|
||||
|
||||
@emph{x86 Windows Options}
|
||||
@gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
|
||||
|
@ -23708,6 +23709,13 @@ locations are @samp{global} for global canary or @samp{tls} for per-thread
|
|||
canary in the TLS block (the default). This option has effect only when
|
||||
@option{-fstack-protector} or @option{-fstack-protector-all} is specified.
|
||||
|
||||
@item -mmitigate-rop
|
||||
@opindex mmitigate-rop
|
||||
Try to avoid generating code sequences that contain unintended return
|
||||
opcodes, to mitigate against certain forms of attack. At the moment,
|
||||
this option is limited in what it can do and should not be relied
|
||||
on to provide serious protection.
|
||||
|
||||
@end table
|
||||
|
||||
These @samp{-m} switches are supported in addition to the above
|
||||
|
|
|
@ -227,13 +227,10 @@ create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
|
|||
struct du_chain *this_du;
|
||||
int nregs;
|
||||
|
||||
memset (head, 0, sizeof *head);
|
||||
head->next_chain = open_chains;
|
||||
head->regno = this_regno;
|
||||
head->nregs = this_nregs;
|
||||
head->need_caller_save_reg = 0;
|
||||
head->cannot_rename = 0;
|
||||
head->renamed = 0;
|
||||
head->tied_chain = NULL;
|
||||
|
||||
id_to_chain.safe_push (head);
|
||||
head->id = current_id++;
|
||||
|
|
|
@ -49,6 +49,10 @@ struct du_head
|
|||
unsigned int cannot_rename:1;
|
||||
/* Nonzero if the chain has already been renamed. */
|
||||
unsigned int renamed:1;
|
||||
|
||||
/* Fields for use by target code. */
|
||||
unsigned int target_data_1;
|
||||
unsigned int target_data_2;
|
||||
};
|
||||
|
||||
typedef struct du_head *du_head_p;
|
||||
|
|
Loading…
Add table
Reference in a new issue