Add generic part for Intel CET enabling. The spec is available at
authorIgor Tsimbalist <igor.v.tsimbalist@intel.com>
Fri, 20 Oct 2017 13:09:38 +0000 (15:09 +0200)
committerIgor Tsimbalist <itsimbal@gcc.gnu.org>
Fri, 20 Oct 2017 13:09:38 +0000 (15:09 +0200)
https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf

A proposal is to introduce a target independent flag
-fcf-protection=[none|branch|return|full] with a semantic to
instrument a code to control validness or integrity of control-flow
transfers using jump and call instructions. The main goal is to detect
and block a possible malware execution through transfer the execution
to unknown target address. Implementation could be either software or
target based. Any target platforms can provide their implementation
for instrumentation under this option.

The compiler should instrument any control-flow transfer points in a
program (ex. call/jmp/ret) as well as any landing pads, which are
targets of control-flow transfers.

A new 'nocf_check' attribute is introduced to provide hand tuning
support. The attribute directs the compiler to skip a call to a
function and a function's landing pad from instrumentation. The
attribute can be used for function and pointer to function types,
otherwise it will be ignored.

Currently all platforms except i386 will report the error and do no
instrumentation. i386 will provide the implementation based on a
specification published by Intel for a new technology called
Control-flow Enforcement Technology (CET).

gcc/c-family/
* c-attribs.c (handle_nocf_check_attribute): New function.
(c_common_attribute_table): Add 'nocf_check' handling.

gcc/c/
* gimple-parser.c: Add second argument NULL to
gimple_build_call_from_tree.

gcc/
* attrib.c (comp_type_attributes): Check nocf_check attribute.
* cfgexpand.c (expand_call_stmt): Set REG_CALL_NOCF_CHECK for
call insn.
* combine.c (distribute_notes): Add REG_CALL_NOCF_CHECK handling.
* common.opt: Add fcf-protection flag.
* emit-rtl.c (try_split): Add REG_CALL_NOCF_CHECK handling.
* flag-types.h: Add enum cf_protection_level.
* gimple.c (gimple_build_call_from_tree): Add second parameter.
Add 'nocf_check' attribute propagation to gimple call.
* gimple.h (gf_mask): Add GF_CALL_NOCF_CHECK.
(gimple_build_call_from_tree): Update prototype.
(gimple_call_nocf_check_p): New function.
(gimple_call_set_nocf_check): Likewise.
* gimplify.c: Add second argument to gimple_build_call_from_tree.
* ipa-icf.c: Add nocf_check attribute in statement hash.
* recog.c (peep2_attempt): Add REG_CALL_NOCF_CHECK handling.
* reg-notes.def: Add REG_NOTE (CALL_NOCF_CHECK).
* toplev.c (process_options): Add flag_cf_protection handling.

From-SVN: r253936

16 files changed:
gcc/ChangeLog
gcc/attribs.c
gcc/c-family/c-attribs.c
gcc/c/gimple-parser.c
gcc/cfgexpand.c
gcc/combine.c
gcc/common.opt
gcc/emit-rtl.c
gcc/flag-types.h
gcc/gimple.c
gcc/gimple.h
gcc/gimplify.c
gcc/ipa-icf.c
gcc/recog.c
gcc/reg-notes.def
gcc/toplev.c

index 184a6294bf16cbffa80ae9e3e45d55467de7604c..4d2bb892ad7912e7bc792e4a8d26bdf809da906b 100644 (file)
@@ -1,3 +1,28 @@
+2017-10-20  Igor Tsimbalist  <igor.v.tsimbalist@intel.com>
+
+       * c-attribs.c (handle_nocf_check_attribute): New function.
+       (c_common_attribute_table): Add 'nocf_check' handling.
+       * gimple-parser.c: Add second argument NULL to
+       gimple_build_call_from_tree.
+       * attrib.c (comp_type_attributes): Check nocf_check attribute.
+       * cfgexpand.c (expand_call_stmt): Set REG_CALL_NOCF_CHECK for
+       call insn.
+       * combine.c (distribute_notes): Add REG_CALL_NOCF_CHECK handling.
+       * common.opt: Add fcf-protection flag.
+       * emit-rtl.c (try_split): Add REG_CALL_NOCF_CHECK handling.
+       * flag-types.h: Add enum cf_protection_level.
+       * gimple.c (gimple_build_call_from_tree): Add second parameter.
+       Add 'nocf_check' attribute propagation to gimple call.
+       * gimple.h (gf_mask): Add GF_CALL_NOCF_CHECK.
+       (gimple_build_call_from_tree): Update prototype.
+       (gimple_call_nocf_check_p): New function.
+       (gimple_call_set_nocf_check): Likewise.
+       * gimplify.c: Add second argument to gimple_build_call_from_tree.
+       * ipa-icf.c: Add nocf_check attribute in statement hash.
+       * recog.c (peep2_attempt): Add REG_CALL_NOCF_CHECK handling.
+       * reg-notes.def: Add REG_NOTE (CALL_NOCF_CHECK).
+       * toplev.c (process_options): Add flag_cf_protection handling.
+
 2017-10-19  Jan Hubicka  <hubicka@ucw.cz>
 
        * x86-tune-costs.h (core_cost): Fix div, move and sqrt latencies.
index 4ef35b861f8f23a5d16975c23490fefd5b365c2e..ed76a8dab6f2bbc019049a366a4f07d32269c0c7 100644 (file)
@@ -1182,6 +1182,9 @@ comp_type_attributes (const_tree type1, const_tree type2)
     }
   if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a)))
     return 0;
+  if ((lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type1)) != NULL)
+      ^ (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type2)) != NULL))
+    return 0;
   /* As some type combinations - like default calling-convention - might
      be compatible, we have to call the target hook to get the final result.  */
   return targetm.comp_type_attributes (type1, type2);
index bd8ca306c2d2793501d5a5b26e1d9a9f01bf2bea..bb75cba4c39d178eff51bd2028140ef068db9e2a 100644 (file)
@@ -65,6 +65,7 @@ static tree handle_asan_odr_indicator_attribute (tree *, tree, tree, int,
 static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
+static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noicf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noipa_attribute (tree *, tree, tree, int, bool *);
 static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
@@ -367,6 +368,8 @@ const struct attribute_spec c_common_attribute_table[] =
   { "patchable_function_entry",        1, 2, true, false, false,
                              handle_patchable_function_entry_attribute,
                              false },
+  { "nocf_check",                    0, 0, false, true, true,
+                             handle_nocf_check_attribute, true },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
@@ -772,6 +775,30 @@ handle_noclone_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "nocf_check" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_nocf_check_attribute (tree *node, tree name,
+                         tree ARG_UNUSED (args),
+                         int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_TYPE
+      && TREE_CODE (*node) != METHOD_TYPE)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (!(flag_cf_protection & CF_BRANCH))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored. Use "
+                               "-fcf-protection option to enable it", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_icf" attribute; arguments as in
    struct attribute_spec.handler.  */
 
index 22f58f4e8207ba8b7fa252d94001cf7f5089bd3b..c2e31df218cc8526b07296500e7b74c79e34f874 100644 (file)
@@ -276,7 +276,7 @@ c_parser_gimple_statement (c_parser *parser, gimple_seq *seq)
       && TREE_CODE (lhs.value) == CALL_EXPR)
     {
       gimple *call;
-      call = gimple_build_call_from_tree (lhs.value);
+      call = gimple_build_call_from_tree (lhs.value, NULL);
       gimple_seq_add_stmt (seq, call);
       gimple_set_location (call, loc);
       return;
@@ -407,7 +407,7 @@ c_parser_gimple_statement (c_parser *parser, gimple_seq *seq)
       rhs = c_parser_gimple_unary_expression (parser);
       if (rhs.value != error_mark_node)
        {
-         gimple *call = gimple_build_call_from_tree (rhs.value);
+         gimple *call = gimple_build_call_from_tree (rhs.value, NULL);
          gimple_call_set_lhs (call, lhs.value);
          gimple_seq_add_stmt (seq, call);
          gimple_set_location (call, loc);
index a255ac3c6f094c3a0a3b5e4e64c02114a70eeb7b..d0e078214895477a37efd447a37deb599feaa06f 100644 (file)
@@ -2658,12 +2658,28 @@ expand_call_stmt (gcall *stmt)
          }
     }
 
+  rtx_insn *before_call = get_last_insn ();
   lhs = gimple_call_lhs (stmt);
   if (lhs)
     expand_assignment (lhs, exp, false);
   else
     expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
+  /* If the gimple call is an indirect call and has 'nocf_check'
+     attribute find a generated CALL insn to mark it as no
+     control-flow verification is needed.  */
+  if (gimple_call_nocf_check_p (stmt)
+      && !gimple_call_fndecl (stmt))
+    {
+      rtx_insn *last = get_last_insn ();
+      while (!CALL_P (last)
+            && last != before_call)
+       last = PREV_INSN (last);
+
+      if (last != before_call)
+       add_reg_note (last, REG_CALL_NOCF_CHECK, const0_rtx);
+    }
+
   mark_transaction_restart_calls (stmt);
 }
 
index 757ae6fc93e48bd2a7a289c1009a27c01d3232f1..a58bb1e9470c3eab338538defbe4722de1b12478 100644 (file)
@@ -14185,6 +14185,7 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2,
        case REG_SETJMP:
        case REG_TM:
        case REG_CALL_DECL:
+       case REG_CALL_NOCF_CHECK:
          /* These notes must remain with the call.  It should not be
             possible for both I2 and I3 to be a call.  */
          if (CALL_P (i3))
index c95da64017465a9ca09f393d3c6e0c8ab085df59..8d62ec5044d5c63e659cddc08b4fe076b1afbe90 100644 (file)
@@ -1620,6 +1620,29 @@ finline-atomics
 Common Report Var(flag_inline_atomics) Init(1) Optimization
 Inline __atomic operations when a lock free instruction sequence is available.
 
+fcf-protection
+Common RejectNegative Alias(fcf-protection=,full)
+
+fcf-protection=
+Common Report Joined RejectNegative Enum(cf_protection_level) Var(flag_cf_protection) Init(CF_NONE)
+-fcf-protection=[full|branch|return|none]      Instrument functions with checks to verify jump/call/return control-flow transfer
+instructions have valid targets.
+
+Enum
+Name(cf_protection_level) Type(enum cf_protection_level) UnknownError(unknown Cotrol-Flow Protection Level %qs)
+
+EnumValue
+Enum(cf_protection_level) String(full) Value(CF_FULL)
+
+EnumValue
+Enum(cf_protection_level) String(branch) Value(CF_BRANCH)
+
+EnumValue
+Enum(cf_protection_level) String(return) Value(CF_RETURN)
+
+EnumValue
+Enum(cf_protection_level) String(none) Value(CF_NONE)
+
 finstrument-functions
 Common Report Var(flag_instrument_function_entry_exit)
 Instrument function entry and exit with profiling calls.
index e790cbcb6ff0989453ed618563c3a12435fbf713..d91988e902d456463b38c496a8216ed6b1d249a5 100644 (file)
@@ -3789,6 +3789,7 @@ try_split (rtx pat, rtx_insn *trial, int last)
        case REG_NORETURN:
        case REG_SETJMP:
        case REG_TM:
+       case REG_CALL_NOCF_CHECK:
          for (insn = insn_last; insn != NULL_RTX; insn = PREV_INSN (insn))
            {
              if (CALL_P (insn))
index bfea17408cb5954f3a2fc5634959bfd0fa411fc2..2b2302963f0511d289b703d626c1a90ed271577f 100644 (file)
@@ -326,4 +326,13 @@ enum gfc_convert
 };
 
 
+/* Control-Flow Protection values.  */
+enum cf_protection_level
+{
+  CF_NONE = 0,
+  CF_BRANCH = 1 << 0,
+  CF_RETURN = 1 << 1,
+  CF_FULL = CF_BRANCH | CF_RETURN,
+  CF_SET = 1 << 2
+};
 #endif /* ! GCC_FLAG_TYPES_H */
index aaf1cc2060fcee5b824e2afceca89feed9adfabc..1f291e10286d4a7a9b66ca0fce84b56b2e826b74 100644 (file)
@@ -346,7 +346,7 @@ gimple_build_call_internal_vec (enum internal_fn fn, vec<tree> args)
    this fact.  */
 
 gcall *
-gimple_build_call_from_tree (tree t)
+gimple_build_call_from_tree (tree t, tree fnptrtype)
 {
   unsigned i, nargs;
   gcall *call;
@@ -379,6 +379,23 @@ gimple_build_call_from_tree (tree t)
   gimple_set_no_warning (call, TREE_NO_WARNING (t));
   gimple_call_set_with_bounds (call, CALL_WITH_BOUNDS_P (t));
 
+  if (fnptrtype)
+    {
+      gimple_call_set_fntype (call, TREE_TYPE (fnptrtype));
+
+      /* Check if it's an indirect CALL and the type has the
+        nocf_check attribute. In that case propagate the information
+        to the gimple CALL insn.  */
+      if (!fndecl)
+       {
+         gcc_assert (POINTER_TYPE_P (fnptrtype));
+         tree fntype = TREE_TYPE (fnptrtype);
+
+         if (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (fntype)))
+           gimple_call_set_nocf_check (call, TRUE);
+       }
+    }
+
   return call;
 }
 
index 6213c49b91f4613b6b3e6795815809b451e5a543..334def89398edcc6908662de735796057b074be3 100644 (file)
@@ -148,6 +148,7 @@ enum gf_mask {
     GF_CALL_WITH_BOUNDS        = 1 << 8,
     GF_CALL_MUST_TAIL_CALL     = 1 << 9,
     GF_CALL_BY_DESCRIPTOR      = 1 << 10,
+    GF_CALL_NOCF_CHECK         = 1 << 11,
     GF_OMP_PARALLEL_COMBINED   = 1 << 0,
     GF_OMP_PARALLEL_GRID_PHONY = 1 << 1,
     GF_OMP_TASK_TASKLOOP       = 1 << 0,
@@ -1425,7 +1426,7 @@ gcall *gimple_build_call (tree, unsigned, ...);
 gcall *gimple_build_call_valist (tree, unsigned, va_list);
 gcall *gimple_build_call_internal (enum internal_fn, unsigned, ...);
 gcall *gimple_build_call_internal_vec (enum internal_fn, vec<tree> );
-gcall *gimple_build_call_from_tree (tree);
+gcall *gimple_build_call_from_tree (tree, tree);
 gassign *gimple_build_assign (tree, tree CXX_MEM_STAT_INFO);
 gassign *gimple_build_assign (tree, enum tree_code,
                              tree, tree, tree CXX_MEM_STAT_INFO);
@@ -2893,6 +2894,25 @@ gimple_call_set_with_bounds (gimple *gs, bool with_bounds)
 }
 
 
+/* Return true if call GS is marked as nocf_check.  */
+
+static inline bool
+gimple_call_nocf_check_p (const gcall *gs)
+{
+  return (gs->subcode & GF_CALL_NOCF_CHECK) != 0;
+}
+
+/* Mark statement GS as nocf_check call.  */
+
+static inline void
+gimple_call_set_nocf_check (gcall *gs, bool nocf_check)
+{
+  if (nocf_check)
+    gs->subcode |= GF_CALL_NOCF_CHECK;
+  else
+    gs->subcode &= ~GF_CALL_NOCF_CHECK;
+}
+
 /* Return the target of internal call GS.  */
 
 static inline enum internal_fn
index 2c1ec852210a413f4565951bb398604c7ffb864b..4c245103fc49fd2c80b9e36d82fe9b7f4545d485 100644 (file)
@@ -3378,8 +3378,7 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
       /* The CALL_EXPR in *EXPR_P is already in GIMPLE form, so all we
         have to do is replicate it as a GIMPLE_CALL tuple.  */
       gimple_stmt_iterator gsi;
-      call = gimple_build_call_from_tree (*expr_p);
-      gimple_call_set_fntype (call, TREE_TYPE (fnptrtype));
+      call = gimple_build_call_from_tree (*expr_p, fnptrtype);
       notice_special_calls (call);
       if (EXPR_CILK_SPAWN (*expr_p))
         gimplify_cilk_detach (pre_p);
@@ -5660,8 +5659,7 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
                                                    CALL_EXPR_ARG (*from_p, 2));
          else
            {
-             call_stmt = gimple_build_call_from_tree (*from_p);
-             gimple_call_set_fntype (call_stmt, TREE_TYPE (fnptrtype));
+             call_stmt = gimple_build_call_from_tree (*from_p, fnptrtype);
            }
        }
       notice_special_calls (call_stmt);
index 4d152ceab1e03241526b6068bec78d9e85b9e186..e666d5ab2d00e8159114046a657b7ea6905da44a 100644 (file)
@@ -1422,6 +1422,7 @@ sem_function::init (void)
              }
          }
 
+       hstate.commit_flag ();
        gcode_hash = hstate.end ();
        bb_sizes.safe_push (nondbg_stmt_count);
 
@@ -1644,6 +1645,11 @@ sem_function::hash_stmt (gimple *stmt, inchash::hash &hstate)
          if (gimple_op (stmt, i))
            add_type (TREE_TYPE (gimple_op (stmt, i)), hstate);
        }
+      /* Consider nocf_check attribute in hash as it affects code
+        generation.  */
+      if (code == GIMPLE_CALL
+         && flag_cf_protection & CF_BRANCH)
+       hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
     default:
       break;
     }
index b8e9b1ba3a83af68ec3f3df95b068f63aafa9350..9aaa6cd7a73ce8737d1ac39baab5649bf12fcc21 100644 (file)
@@ -3381,6 +3381,7 @@ peep2_attempt (basic_block bb, rtx_insn *insn, int match_len, rtx_insn *attempt)
          case REG_NORETURN:
          case REG_SETJMP:
          case REG_TM:
+         case REG_CALL_NOCF_CHECK:
            add_reg_note (new_insn, REG_NOTE_KIND (note),
                          XEXP (note, 0));
            break;
index a542990cde2d6d3c9e9c03185de14cb2c7e30bb2..d83fc45ef72c80e9062b1267b4218a44ee3f6b36 100644 (file)
@@ -232,3 +232,10 @@ REG_NOTE (STACK_CHECK)
    The decl might not be available in the call due to splitting of the call
    insn.  This note is a SYMBOL_REF.  */
 REG_NOTE (CALL_DECL)
+
+/* Indicate that a call should not be verified for control-flow consistency.
+   The target address of the call is assumed as a valid address and no check
+   to validate a branch to the target address is needed.  The call is marked
+   when a called function has a 'notrack' attribute.  This note is used by the
+   compiler when the option -fcf-protection=branch is specified.  */
+REG_NOTE (CALL_NOCF_CHECK)
index ac9d2fc18bb76f81caf24a14cf8f2787947e696f..8c45e1df2af326cbcfc072976f54fff73122c34b 100644 (file)
@@ -1277,6 +1277,32 @@ process_options (void)
           "-floop-parallelize-all)");
 #endif
 
+  if (flag_cf_protection != CF_NONE
+      && !(flag_cf_protection & CF_SET))
+    {
+      if (flag_cf_protection == CF_FULL)
+       {
+         error_at (UNKNOWN_LOCATION,
+                   "%<-fcf-protection=full%> is not supported for this "
+                   "target");
+         flag_cf_protection = CF_NONE;
+       }
+      if (flag_cf_protection == CF_BRANCH)
+       {
+         error_at (UNKNOWN_LOCATION,
+                   "%<-fcf-protection=branch%> is not supported for this "
+                   "target");
+         flag_cf_protection = CF_NONE;
+       }
+      if (flag_cf_protection == CF_RETURN)
+       {
+         error_at (UNKNOWN_LOCATION,
+                   "%<-fcf-protection=return%> is not supported for this "
+                   "target");
+         flag_cf_protection = CF_NONE;
+       }
+    }
+
   if (flag_check_pointer_bounds)
     {
       if (targetm.chkp_bound_mode () == VOIDmode)