From c931e8d5a96463427040b0d11f9c4352ac22b2b0 Mon Sep 17 00:00:00 2001 From: Cooper Qu Date: Mon, 13 Jul 2020 16:15:08 +0800 Subject: [PATCH] RISC-V: Add support for TLS stack protector canary access gcc/ * config/riscv/riscv-opts.h (stack_protector_guard): New enum. * config/riscv/riscv.c (riscv_option_override): Handle the new options. * config/riscv/riscv.md (stack_protect_set): New pattern to handle flexible stack protector guard settings. (stack_protect_set_): Ditto. (stack_protect_test): Ditto. (stack_protect_test_): Ditto. * config/riscv/riscv.opt (mstack-protector-guard=, mstack-protector-guard-reg=, mstack-protector-guard-offset=): New options. * doc/invoke.texi (Option Summary) [RISC-V Options]: Add -mstack-protector-guard=, -mstack-protector-guard-reg=, and -mstack-protector-guard-offset=. (RISC-V Options): Ditto. Signed-off-by: cooper Signed-off-by: Guo Ren --- gcc/config/riscv/riscv-opts.h | 6 +++ gcc/config/riscv/riscv.c | 47 ++++++++++++++++++++ gcc/config/riscv/riscv.md | 80 +++++++++++++++++++++++++++++++++++ gcc/config/riscv/riscv.opt | 28 ++++++++++++ gcc/doc/invoke.texi | 22 +++++++++- 5 files changed, 182 insertions(+), 1 deletion(-) diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h index 8f12e50b9f1..2a3f9d9eef5 100644 --- a/gcc/config/riscv/riscv-opts.h +++ b/gcc/config/riscv/riscv-opts.h @@ -51,4 +51,10 @@ enum riscv_align_data { riscv_align_data_type_natural }; +/* Where to get the canary for the stack protector. */ +enum stack_protector_guard { + SSP_TLS, /* per-thread canary in TLS block */ + SSP_GLOBAL /* global canary */ +}; + #endif /* ! GCC_RISCV_OPTS_H */ diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c index bfb3885ed08..63b0c3877b0 100644 --- a/gcc/config/riscv/riscv.c +++ b/gcc/config/riscv/riscv.c @@ -4775,6 +4775,53 @@ riscv_option_override (void) " [%<-mriscv-attribute%>]"); #endif + if (riscv_stack_protector_guard == SSP_GLOBAL + && global_options_set.x_riscv_stack_protector_guard_offset_str) + { + error ("incompatible options %<-mstack-protector-guard=global%> and " + "%<-mstack-protector-guard-offset=%s%>", + riscv_stack_protector_guard_offset_str); + } + + if (riscv_stack_protector_guard == SSP_TLS + && !(global_options_set.x_riscv_stack_protector_guard_offset_str + && global_options_set.x_riscv_stack_protector_guard_reg_str)) + { + error ("both %<-mstack-protector-guard-offset%> and " + "%<-mstack-protector-guard-reg%> must be used " + "with %<-mstack-protector-guard=sysreg%>"); + } + + if (global_options_set.x_riscv_stack_protector_guard_reg_str) + { + const char *str = riscv_stack_protector_guard_reg_str; + int reg = decode_reg_name (str); + + if (!IN_RANGE (reg, GP_REG_FIRST + 1, GP_REG_LAST)) + error ("%qs is not a valid base register in %qs", str, + "-mstack-protector-guard-reg="); + + riscv_stack_protector_guard_reg = reg; + } + + if (global_options_set.x_riscv_stack_protector_guard_offset_str) + { + char *end; + const char *str = riscv_stack_protector_guard_offset_str; + errno = 0; + long offs = strtol (riscv_stack_protector_guard_offset_str, &end, 0); + + if (!*str || *end || errno) + error ("%qs is not a valid number in %qs", str, + "-mstack-protector-guard-offset="); + + if (!SMALL_OPERAND (offs)) + error ("%qs is not a valid offset in %qs", str, + "-mstack-protector-guard-offset="); + + riscv_stack_protector_guard_offset = offs; + } + } /* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */ diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 95a02ecaa34..f15bad3b29e 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -65,6 +65,10 @@ UNSPECV_BLOCKAGE UNSPECV_FENCE UNSPECV_FENCE_I + + ;; Stack Smash Protector + UNSPEC_SSP_SET + UNSPEC_SSP_TEST ]) (define_constants @@ -2523,6 +2527,82 @@ "" {}) +;; Named patterns for stack smashing protection. + +(define_expand "stack_protect_set" + [(match_operand 0 "memory_operand") + (match_operand 1 "memory_operand")] + "" +{ + machine_mode mode = GET_MODE (operands[0]); + if (riscv_stack_protector_guard == SSP_TLS) + { + rtx reg = gen_rtx_REG (Pmode, riscv_stack_protector_guard_reg); + rtx offset = GEN_INT (riscv_stack_protector_guard_offset); + rtx addr = gen_rtx_PLUS (Pmode, reg, offset); + operands[1] = gen_rtx_MEM (Pmode, addr); + } + + emit_insn ((mode == DImode + ? gen_stack_protect_set_di + : gen_stack_protect_set_si) (operands[0], operands[1])); + DONE; +}) + +;; DO NOT SPLIT THIS PATTERN. It is important for security reasons that the +;; canary value does not live beyond the life of this sequence. +(define_insn "stack_protect_set_" + [(set (match_operand:GPR 0 "memory_operand" "=m") + (unspec:GPR [(match_operand:GPR 1 "memory_operand" "m")] + UNSPEC_SSP_SET)) + (set (match_scratch:GPR 2 "=&r") (const_int 0))] + "" + "\\t%2, %1\;\\t%2, %0\;li\t%2, 0" + [(set_attr "length" "12")]) + +(define_expand "stack_protect_test" + [(match_operand 0 "memory_operand") + (match_operand 1 "memory_operand") + (match_operand 2)] + "" +{ + rtx result; + machine_mode mode = GET_MODE (operands[0]); + + result = gen_reg_rtx(mode); + if (riscv_stack_protector_guard == SSP_TLS) + { + rtx reg = gen_rtx_REG (Pmode, riscv_stack_protector_guard_reg); + rtx offset = GEN_INT (riscv_stack_protector_guard_offset); + rtx addr = gen_rtx_PLUS (Pmode, reg, offset); + operands[1] = gen_rtx_MEM (Pmode, addr); + } + emit_insn ((mode == DImode + ? gen_stack_protect_test_di + : gen_stack_protect_test_si) (result, + operands[0], + operands[1])); + + if (mode == DImode) + emit_jump_insn (gen_cbranchdi4 (gen_rtx_EQ (VOIDmode, result, const0_rtx), + result, const0_rtx, operands[2])); + else + emit_jump_insn (gen_cbranchsi4 (gen_rtx_EQ (VOIDmode, result, const0_rtx), + result, const0_rtx, operands[2])); + + DONE; +}) + +(define_insn "stack_protect_test_" + [(set (match_operand:GPR 0 "register_operand" "=r") + (unspec:GPR [(match_operand:GPR 1 "memory_operand" "m") + (match_operand:GPR 2 "memory_operand" "m")] + UNSPEC_SSP_TEST)) + (clobber (match_scratch:GPR 3 "=&r"))] + "" + "\t%3, %1\;\t%0, %2\;xor\t%0, %3, %0\;li\t%3, 0" + [(set_attr "length" "12")]) + (include "sync.md") (include "peephole.md") (include "pic.md") diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt index e4bfcb86f51..f01d3ab79c3 100644 --- a/gcc/config/riscv/riscv.opt +++ b/gcc/config/riscv/riscv.opt @@ -151,3 +151,31 @@ Enum(riscv_align_data) String(xlen) Value(riscv_align_data_type_xlen) EnumValue Enum(riscv_align_data) String(natural) Value(riscv_align_data_type_natural) + +mstack-protector-guard= +Target RejectNegative Joined Enum(stack_protector_guard) Var(riscv_stack_protector_guard) Init(SSP_GLOBAL) +Use given stack-protector guard. + +Enum +Name(stack_protector_guard) Type(enum stack_protector_guard) +Valid arguments to -mstack-protector-guard=: + +EnumValue +Enum(stack_protector_guard) String(tls) Value(SSP_TLS) + +EnumValue +Enum(stack_protector_guard) String(global) Value(SSP_GLOBAL) + +mstack-protector-guard-reg= +Target RejectNegative Joined Var(riscv_stack_protector_guard_reg_str) +Use the given base register for addressing the stack-protector guard. + +TargetVariable +int riscv_stack_protector_guard_reg = 0 + +mstack-protector-guard-offset= +Target RejectNegative Joined Integer Var(riscv_stack_protector_guard_offset_str) +Use the given offset for addressing the stack-protector guard. + +TargetVariable +long riscv_stack_protector_guard_offset = 0 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index eaaf6d06b65..5a5159d39b3 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1140,7 +1140,9 @@ See RS/6000 and PowerPC Options. -mexplicit-relocs -mno-explicit-relocs @gol -mrelax -mno-relax @gol -mriscv-attribute -mmo-riscv-attribute @gol --malign-data=@var{type}} +-malign-data=@var{type} @gol ++-mstack-protector-guard=@var{guard} -mstack-protector-guard-reg=@var{reg} @gol ++-mstack-protector-guard-offset=@var{offset}} @emph{RL78 Options} @gccoptlist{-msim -mmul=none -mmul=g13 -mmul=g14 -mallregs @gol @@ -25795,6 +25797,24 @@ Control how GCC aligns variables and constants of array, structure, or union types. Supported values for @var{type} are @samp{xlen} which uses x register width as the alignment value, and @samp{natural} which uses natural alignment. @samp{xlen} is the default. + +@item -mstack-protector-guard=@var{guard} +@itemx -mstack-protector-guard-reg=@var{reg} +@itemx -mstack-protector-guard-offset=@var{offset} +@opindex mstack-protector-guard +@opindex mstack-protector-guard-reg +@opindex mstack-protector-guard-offset +Generate stack protection code using canary at @var{guard}. Supported +locations are @samp{global} for a global canary or @samp{tls} for per-thread +canary in the TLS block. + +With the latter choice the options +@option{-mstack-protector-guard-reg=@var{reg}} and +@option{-mstack-protector-guard-offset=@var{offset}} furthermore specify +which register to use as base register for reading the canary, +and from what offset from that base register. There is no default +register or offset as this is entirely for use within the Linux +kernel. @end table @node RL78 Options -- 2.30.2