regrename.h (struct du_head): Add target_data_1 and target_data_2 fields.
authorBernd Schmidt <bernds@redhat.com>
Wed, 18 Nov 2015 12:26:43 +0000 (12:26 +0000)
committerBernd Schmidt <bernds@gcc.gnu.org>
Wed, 18 Nov 2015 12:26:43 +0000 (12:26 +0000)
* 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

gcc/ChangeLog
gcc/config/i386/i386.c
gcc/config/i386/i386.md
gcc/config/i386/i386.opt
gcc/doc/invoke.texi
gcc/regrename.c
gcc/regrename.h

index 2fcf704..abf01d3 100644 (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
index f1a287b..83749d5 100644 (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);
 }
 \f
+/* 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 ();
 
index 34a6d3f..39b058f 100644 (file)
         ]
         (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
    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"
   "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")])
 
   "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")])
 
   ; 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")
   ""
   "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")
   ""
   "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")
index 82de21f..2723c22 100644 (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.
index 8be39b7..7cef176 100644 (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
index 1f11695..e2a1e83 100644 (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++;
index c702835..6fdaff1 100644 (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;