aboutsummaryrefslogtreecommitdiff
path: root/gcc/function.c
diff options
context:
space:
mode:
authorqing zhao <qinzhao@gcc.gnu.org>2020-10-30 20:41:38 +0100
committerqing zhao <qinzhao@gcc.gnu.org>2020-10-30 20:41:38 +0100
commitd10f3e900b0377b4760a090b0f90371bcef01686 (patch)
treef78af058a8e7a4a1c04d601dbda48821a4eaa2e4 /gcc/function.c
parent44fbc9c6e02ca5b8f98f25b514ed7588e7ba733d (diff)
downloadgcc-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.c190
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. */