Add generic part for Intel CET enabling. The spec is available at
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
This commit is contained in:
parent
e64944ac65
commit
5c5f0b65ee
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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 */
|
||||
|
19
gcc/gimple.c
19
gcc/gimple.c
@ -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;
|
||||
}
|
||||
|
||||
|
22
gcc/gimple.h
22
gcc/gimple.h
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
26
gcc/toplev.c
26
gcc/toplev.c
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user