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:
Bernd Schmidt 2015-11-18 12:26:43 +00:00 committed by Bernd Schmidt
parent 98f84050d6
commit d085c46817
7 changed files with 371 additions and 5 deletions

View file

@ -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

View file

@ -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 ();

View file

@ -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")

View file

@ -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.

View file

@ -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

View file

@ -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++;

View file

@ -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;