diff options
author | qing zhao <qinzhao@gcc.gnu.org> | 2020-10-30 20:41:38 +0100 |
---|---|---|
committer | qing zhao <qinzhao@gcc.gnu.org> | 2020-10-30 20:41:38 +0100 |
commit | d10f3e900b0377b4760a090b0f90371bcef01686 (patch) | |
tree | f78af058a8e7a4a1c04d601dbda48821a4eaa2e4 /gcc/function.c | |
parent | 44fbc9c6e02ca5b8f98f25b514ed7588e7ba733d (diff) | |
download | gcc-d10f3e900b0377b4760a090b0f90371bcef01686.zip gcc-d10f3e900b0377b4760a090b0f90371bcef01686.tar.gz gcc-d10f3e900b0377b4760a090b0f90371bcef01686.tar.bz2 |
Add -fzero-call-used-regs option and zero_call_used_regs function attributes.
This new feature causes the compiler to zero a subset of all call-used
registers at function return. This is used to increase program security
by either mitigating Return-Oriented Programming (ROP) attacks or
preventing information leakage through registers.
gcc/ChangeLog:
2020-10-30 Qing Zhao <qing.zhao@oracle.com>
H.J.Lu <hjl.tools@gmail.com>
* common.opt: Add new option -fzero-call-used-regs
* config/i386/i386.c (zero_call_used_regno_p): New function.
(zero_call_used_regno_mode): Likewise.
(zero_all_vector_registers): Likewise.
(zero_all_st_registers): Likewise.
(zero_all_mm_registers): Likewise.
(ix86_zero_call_used_regs): Likewise.
(TARGET_ZERO_CALL_USED_REGS): Define.
* df-scan.c (df_epilogue_uses_p): New function.
(df_get_exit_block_use_set): Replace EPILOGUE_USES with
df_epilogue_uses_p.
* df.h (df_epilogue_uses_p): Declare.
* doc/extend.texi: Document the new zero_call_used_regs attribute.
* doc/invoke.texi: Document the new -fzero-call-used-regs option.
* doc/tm.texi: Regenerate.
* doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGS): New hook.
* emit-rtl.h (struct rtl_data): New field must_be_zero_on_return.
* flag-types.h (namespace zero_regs_flags): New namespace.
* function.c (gen_call_used_regs_seq): New function.
(class pass_zero_call_used_regs): New class.
(pass_zero_call_used_regs::execute): New function.
(make_pass_zero_call_used_regs): New function.
* optabs.c (expand_asm_reg_clobber_mem_blockage): New function.
* optabs.h (expand_asm_reg_clobber_mem_blockage): Declare.
* opts.c (zero_call_used_regs_opts): New structure array
initialization.
(parse_zero_call_used_regs_options): New function.
(common_handle_option): Handle -fzero-call-used-regs.
* opts.h (zero_call_used_regs_opts): New structure array.
* passes.def: Add new pass pass_zero_call_used_regs.
* recog.c (valid_insn_p): New function.
* recog.h (valid_insn_p): Declare.
* resource.c (init_resource_info): Replace EPILOGUE_USES with
df_epilogue_uses_p.
* target.def (zero_call_used_regs): New hook.
* targhooks.c (default_zero_call_used_regs): New function.
* targhooks.h (default_zero_call_used_regs): Declare.
* tree-pass.h (make_pass_zero_call_used_regs): Declare.
gcc/c-family/ChangeLog:
2020-10-30 Qing Zhao <qing.zhao@oracle.com>
H.J.Lu <hjl.tools@gmail.com>
* c-attribs.c (c_common_attribute_table): Add new attribute
zero_call_used_regs.
(handle_zero_call_used_regs_attribute): New function.
gcc/testsuite/ChangeLog:
2020-10-30 Qing Zhao <qing.zhao@oracle.com>
H.J.Lu <hjl.tools@gmail.com>
* c-c++-common/zero-scratch-regs-1.c: New test.
* c-c++-common/zero-scratch-regs-10.c: New test.
* c-c++-common/zero-scratch-regs-11.c: New test.
* c-c++-common/zero-scratch-regs-2.c: New test.
* c-c++-common/zero-scratch-regs-3.c: New test.
* c-c++-common/zero-scratch-regs-4.c: New test.
* c-c++-common/zero-scratch-regs-5.c: New test.
* c-c++-common/zero-scratch-regs-6.c: New test.
* c-c++-common/zero-scratch-regs-7.c: New test.
* c-c++-common/zero-scratch-regs-8.c: New test.
* c-c++-common/zero-scratch-regs-9.c: New test.
* c-c++-common/zero-scratch-regs-attr-usages.c: New test.
* gcc.target/i386/zero-scratch-regs-1.c: New test.
* gcc.target/i386/zero-scratch-regs-10.c: New test.
* gcc.target/i386/zero-scratch-regs-11.c: New test.
* gcc.target/i386/zero-scratch-regs-12.c: New test.
* gcc.target/i386/zero-scratch-regs-13.c: New test.
* gcc.target/i386/zero-scratch-regs-14.c: New test.
* gcc.target/i386/zero-scratch-regs-15.c: New test.
* gcc.target/i386/zero-scratch-regs-16.c: New test.
* gcc.target/i386/zero-scratch-regs-17.c: New test.
* gcc.target/i386/zero-scratch-regs-18.c: New test.
* gcc.target/i386/zero-scratch-regs-19.c: New test.
* gcc.target/i386/zero-scratch-regs-2.c: New test.
* gcc.target/i386/zero-scratch-regs-20.c: New test.
* gcc.target/i386/zero-scratch-regs-21.c: New test.
* gcc.target/i386/zero-scratch-regs-22.c: New test.
* gcc.target/i386/zero-scratch-regs-23.c: New test.
* gcc.target/i386/zero-scratch-regs-24.c: New test.
* gcc.target/i386/zero-scratch-regs-25.c: New test.
* gcc.target/i386/zero-scratch-regs-26.c: New test.
* gcc.target/i386/zero-scratch-regs-27.c: New test.
* gcc.target/i386/zero-scratch-regs-28.c: New test.
* gcc.target/i386/zero-scratch-regs-29.c: New test.
* gcc.target/i386/zero-scratch-regs-30.c: New test.
* gcc.target/i386/zero-scratch-regs-31.c: New test.
* gcc.target/i386/zero-scratch-regs-3.c: New test.
* gcc.target/i386/zero-scratch-regs-4.c: New test.
* gcc.target/i386/zero-scratch-regs-5.c: New test.
* gcc.target/i386/zero-scratch-regs-6.c: New test.
* gcc.target/i386/zero-scratch-regs-7.c: New test.
* gcc.target/i386/zero-scratch-regs-8.c: New test.
* gcc.target/i386/zero-scratch-regs-9.c: New test.
Diffstat (limited to 'gcc/function.c')
-rw-r--r-- | gcc/function.c | 190 |
1 files changed, 189 insertions, 1 deletions
diff --git a/gcc/function.c b/gcc/function.c index f903a1e..004fa38 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -46,10 +46,12 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "expmed.h" #include "optabs.h" +#include "opts.h" #include "regs.h" #include "emit-rtl.h" #include "recog.h" #include "rtl-error.h" +#include "hard-reg-set.h" #include "alias.h" #include "fold-const.h" #include "stor-layout.h" @@ -5815,6 +5817,103 @@ make_prologue_seq (void) return seq; } +/* Emit a sequence of insns to zero the call-used registers before RET + according to ZERO_REGS_TYPE. */ + +static void +gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type) +{ + bool only_gpr = true; + bool only_used = true; + bool only_arg = true; + + /* No need to zero call-used-regs in main (). */ + if (MAIN_NAME_P (DECL_NAME (current_function_decl))) + return; + + /* No need to zero call-used-regs if __builtin_eh_return is called + since it isn't a normal function return. */ + if (crtl->calls_eh_return) + return; + + /* If only_gpr is true, only zero call-used registers that are + general-purpose registers; if only_used is true, only zero + call-used registers that are used in the current function; + if only_arg is true, only zero call-used registers that pass + parameters defined by the flatform's calling conversion. */ + + using namespace zero_regs_flags; + + only_gpr = zero_regs_type & ONLY_GPR; + only_used = zero_regs_type & ONLY_USED; + only_arg = zero_regs_type & ONLY_ARG; + + /* For each of the hard registers, we should zero it if: + 1. it is a call-used register; + and 2. it is not a fixed register; + and 3. it is not live at the return of the routine; + and 4. it is general registor if only_gpr is true; + and 5. it is used in the routine if only_used is true; + and 6. it is a register that passes parameter if only_arg is true. */ + + /* First, prepare the data flow information. */ + basic_block bb = BLOCK_FOR_INSN (ret); + auto_bitmap live_out; + bitmap_copy (live_out, df_get_live_out (bb)); + df_simulate_initialize_backwards (bb, live_out); + df_simulate_one_insn_backwards (bb, ret, live_out); + + HARD_REG_SET selected_hardregs; + CLEAR_HARD_REG_SET (selected_hardregs); + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!crtl->abi->clobbers_full_reg_p (regno)) + continue; + if (fixed_regs[regno]) + continue; + if (REGNO_REG_SET_P (live_out, regno)) + continue; + if (only_gpr + && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno)) + continue; + if (only_used && !df_regs_ever_live_p (regno)) + continue; + if (only_arg && !FUNCTION_ARG_REGNO_P (regno)) + continue; + + /* Now this is a register that we might want to zero. */ + SET_HARD_REG_BIT (selected_hardregs, regno); + } + + if (hard_reg_set_empty_p (selected_hardregs)) + return; + + /* Now that we have a hard register set that needs to be zeroed, pass it to + target to generate zeroing sequence. */ + HARD_REG_SET zeroed_hardregs; + start_sequence (); + zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs); + rtx_insn *seq = get_insns (); + end_sequence (); + if (seq) + { + /* Emit the memory blockage and register clobber asm volatile before + the whole sequence. */ + start_sequence (); + expand_asm_reg_clobber_mem_blockage (zeroed_hardregs); + rtx_insn *seq_barrier = get_insns (); + end_sequence (); + + emit_insn_before (seq_barrier, ret); + emit_insn_before (seq, ret); + + /* Update the data flow information. */ + crtl->must_be_zero_on_return |= zeroed_hardregs; + df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun)); + } +} + + /* Return a sequence to be used as the epilogue for the current function, or NULL. */ @@ -6486,7 +6585,96 @@ make_pass_thread_prologue_and_epilogue (gcc::context *ctxt) { return new pass_thread_prologue_and_epilogue (ctxt); } - + +namespace { + +const pass_data pass_data_zero_call_used_regs = +{ + RTL_PASS, /* type */ + "zero_call_used_regs", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_zero_call_used_regs: public rtl_opt_pass +{ +public: + pass_zero_call_used_regs (gcc::context *ctxt) + : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_zero_call_used_regs + +unsigned int +pass_zero_call_used_regs::execute (function *fun) +{ + using namespace zero_regs_flags; + unsigned int zero_regs_type = UNSET; + + tree attr_zero_regs = lookup_attribute ("zero_call_used_regs", + DECL_ATTRIBUTES (fun->decl)); + + /* Get the type of zero_call_used_regs from function attribute. + We have filtered out invalid attribute values already at this point. */ + if (attr_zero_regs) + { + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE + is the attribute argument's value. */ + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST); + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST); + + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (attr_zero_regs), + zero_call_used_regs_opts[i].name) == 0) + { + zero_regs_type = zero_call_used_regs_opts[i].flag; + break; + } + } + + if (!zero_regs_type) + zero_regs_type = flag_zero_call_used_regs; + + /* No need to zero call-used-regs when no user request is present. */ + if (!(zero_regs_type & ENABLED)) + return 0; + + edge_iterator ei; + edge e; + + /* This pass needs data flow information. */ + df_analyze (); + + /* Iterate over the function's return instructions and insert any + register zeroing required by the -fzero-call-used-regs command-line + option or the "zero_call_used_regs" function attribute. */ + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + rtx_insn *insn = BB_END (e->src); + if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn))) + gen_call_used_regs_seq (insn, zero_regs_type); + } + + return 0; +} + +} // anon namespace + +rtl_opt_pass * +make_pass_zero_call_used_regs (gcc::context *ctxt) +{ + return new pass_zero_call_used_regs (ctxt); +} /* If CONSTRAINT is a matching constraint, then return its number. Otherwise, return -1. */ |