'Retpoline' mitigation technique for Spectre (branch target injection) [CVE-2017-5715]: https://security.googleblog.com/2018/01/more-details-about-mitigations-for-cpu_4.html https://support.google.com/faqs/answer/7625886 https://spectreattack.com/ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5715 Patch copied from the 'retpoline-20180107' branch of upstream source repository (please add new / update existing patches when new 'retpoline-xxxxxxxx' branch appears): http://git.infradead.org/users/dwmw2/gcc-retpoline.git From e9794727bb0384be6d27ad1edaefc71c23cc0d86 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 28 Nov 2017 06:10:39 -0800 Subject: [PATCH 08/17] Add indirect_branch attribute with tests __attribute__ ((indirect_branch("thunk"))) __attribute__ ((indirect_branch("thunk-inline"))) __attribute__ ((indirect_branch("thunk-extern"))) __attribute__ ((indirect_branch("keep"))) --- gcc/config/i386/i386-opts.h | 1 + gcc/config/i386/i386.c | 74 ++++++++++++++++++++-- gcc/config/i386/i386.h | 3 + .../gcc.target/i386/indirect-thunk-attr-1.c | 22 +++++++ .../gcc.target/i386/indirect-thunk-attr-2.c | 20 ++++++ .../gcc.target/i386/indirect-thunk-attr-3.c | 21 ++++++ .../gcc.target/i386/indirect-thunk-attr-4.c | 20 ++++++ .../gcc.target/i386/indirect-thunk-attr-5.c | 22 +++++++ .../gcc.target/i386/indirect-thunk-attr-6.c | 21 ++++++ .../gcc.target/i386/indirect-thunk-attr-7.c | 44 +++++++++++++ .../gcc.target/i386/indirect-thunk-attr-8.c | 41 ++++++++++++ 11 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c diff --git a/gcc/config/i386/i386-opts.h b/gcc/config/i386/i386-opts.h index f8d80ba7ec6..9e56d7f2d12 100644 --- a/gcc/config/i386/i386-opts.h +++ b/gcc/config/i386/i386-opts.h @@ -100,6 +100,7 @@ enum stack_protector_guard { }; enum indirect_branch { + indirect_branch_unset = 0, indirect_branch_keep, indirect_branch_thunk, indirect_branch_thunk_inline, diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index ac542f79846..5e66af08066 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7137,6 +7137,37 @@ ix86_set_func_type (tree fndecl) } } +/* Set the indirect_branch_type field from the function FNDECL. */ + +static void +ix86_set_indirect_branch_type (tree fndecl) +{ + if (cfun->machine->indirect_branch_type == indirect_branch_unset) + { + tree attr = lookup_attribute ("indirect_branch", + DECL_ATTRIBUTES (fndecl)); + if (attr != NULL) + { + tree args = TREE_VALUE (attr); + if (args == NULL) + gcc_unreachable (); + tree cst = TREE_VALUE (args); + if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0) + cfun->machine->indirect_branch_type = indirect_branch_keep; + else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0) + cfun->machine->indirect_branch_type = indirect_branch_thunk; + else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0) + cfun->machine->indirect_branch_type = indirect_branch_thunk_inline; + else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0) + cfun->machine->indirect_branch_type = indirect_branch_thunk_extern; + else + gcc_unreachable (); + } + else + cfun->machine->indirect_branch_type = ix86_indirect_branch; + } +} + /* Establish appropriate back-end context for processing the function FNDECL. The argument might be NULL to indicate processing at top level, outside of any function scope. */ @@ -7152,7 +7183,10 @@ ix86_set_current_function (tree fndecl) one is extern inline and one isn't. Call ix86_set_func_type to set the func_type field. */ if (fndecl != NULL_TREE) - ix86_set_func_type (fndecl); + { + ix86_set_func_type (fndecl); + ix86_set_indirect_branch_type (fndecl); + } return; } @@ -7172,6 +7206,7 @@ ix86_set_current_function (tree fndecl) } ix86_set_func_type (fndecl); + ix86_set_indirect_branch_type (fndecl); tree new_tree = DECL_FUNCTION_SPECIFIC_TARGET (fndecl); if (new_tree == NULL_TREE) @@ -28605,9 +28640,11 @@ ix86_output_indirect_branch (rtx call_op, const char *xasm, char push_buf[64]; bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn); - if (ix86_indirect_branch != indirect_branch_thunk_inline) + if (cfun->machine->indirect_branch_type + != indirect_branch_thunk_inline) { - bool need_thunk = ix86_indirect_branch == indirect_branch_thunk; + bool need_thunk + = cfun->machine->indirect_branch_type == indirect_branch_thunk; if (need_bnd_p) indirect_thunk_bnd_needed |= need_thunk; else @@ -28716,7 +28753,7 @@ const char * ix86_output_indirect_jmp (rtx call_op) { if (ix86_red_zone_size == 0 - && ix86_indirect_branch != indirect_branch_keep) + && cfun->machine->indirect_branch_type != indirect_branch_keep) { ix86_output_indirect_branch (call_op, "%0", true); return ""; @@ -28733,7 +28770,7 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) bool direct_p = constant_call_address_operand (call_op, VOIDmode); bool output_indirect_p = (!TARGET_SEH - && ix86_indirect_branch != indirect_branch_keep); + && cfun->machine->indirect_branch_type != indirect_branch_keep); bool seh_nop_p = false; const char *xasm; @@ -41749,7 +41786,7 @@ ix86_handle_struct_attribute (tree *node, tree name, tree, int, } static tree -ix86_handle_fndecl_attribute (tree *node, tree name, tree, int, +ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) @@ -41758,6 +41795,29 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree, int, name); *no_add_attrs = true; } + + if (is_attribute_p ("indirect_branch", name)) + { + tree cst = TREE_VALUE (args); + if (TREE_CODE (cst) != STRING_CST) + { + warning (OPT_Wattributes, + "%qE attribute requires a string constant argument", + name); + *no_add_attrs = true; + } + else if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) + { + warning (OPT_Wattributes, + "argument to %qE attribute is not " + "(keep|thunk|thunk-inline|thunk-extern)", name); + *no_add_attrs = true; + } + } + return NULL_TREE; } @@ -46052,6 +46112,8 @@ static const struct attribute_spec ix86_attribute_table[] = ix86_handle_interrupt_attribute, false }, { "no_caller_saved_registers", 0, 0, false, true, true, ix86_handle_no_caller_saved_registers_attribute, false }, + { "indirect_branch", 1, 1, true, false, false, + ix86_handle_fndecl_attribute, false }, /* End element. */ { NULL, 0, 0, false, false, false, NULL, false } diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 7d9f9020fb3..a9c199a107c 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -2604,6 +2604,9 @@ struct GTY(()) machine_function { /* Function type. */ ENUM_BITFIELD(function_type) func_type : 2; + /* How to generate indirec branch. */ + ENUM_BITFIELD(indirect_branch) indirect_branch_type : 3; + /* If true, the current function is a function specified with the "interrupt" or "no_caller_saved_registers" attribute. */ BOOL_BITFIELD no_caller_saved_registers : 1; diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c new file mode 100644 index 00000000000..26550fad4c8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-1.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch; + +extern void male_indirect_jump (long) + __attribute__ ((indirect_branch("thunk"))); + +void +male_indirect_jump (long offset) +{ + dispatch(offset); +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c new file mode 100644 index 00000000000..f57bb2a92d6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-2.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch[256]; + +__attribute__ ((indirect_branch("thunk"))) +void +male_indirect_jump (long offset) +{ + dispatch[offset](offset); +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c new file mode 100644 index 00000000000..a3668a6586c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-3.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch; +extern int male_indirect_jump (long) + __attribute__ ((indirect_branch("thunk-inline"))); + +int +male_indirect_jump (long offset) +{ + dispatch(offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-not "__x86.indirect_thunk" } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c new file mode 100644 index 00000000000..a9c4a137dd4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-4.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch[256]; + +__attribute__ ((indirect_branch("thunk-inline"))) +int +male_indirect_jump (long offset) +{ + dispatch[offset](offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 2 } } */ +/* { dg-final { scan-assembler-not "__x86.indirect_thunk" } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c new file mode 100644 index 00000000000..9582e0c5824 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-5.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch; +extern int male_indirect_jump (long) + __attribute__ ((indirect_branch("thunk-extern"))); + +int +male_indirect_jump (long offset) +{ + dispatch(offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c new file mode 100644 index 00000000000..66442cacfe8 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-6.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +typedef void (*dispatch_t)(long offset); + +dispatch_t dispatch[256]; + +__attribute__ ((indirect_branch("thunk-extern"))) +int +male_indirect_jump (long offset) +{ + dispatch[offset](offset); + return 0; +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*_?dispatch" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler-times "jmp\[ \t\]*\.LIND" 1 } } */ +/* { dg-final { scan-assembler-times "call\[ \t\]*\.LIND" 1 } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c new file mode 100644 index 00000000000..2a19b54cd2e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-7.c @@ -0,0 +1,44 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-pic" } */ + +void func0 (void); +void func1 (void); +void func2 (void); +void func3 (void); +void func4 (void); +void func4 (void); +void func5 (void); + +__attribute__ ((indirect_branch("thunk-extern"))) +void +bar (int i) +{ + switch (i) + { + default: + func0 (); + break; + case 1: + func1 (); + break; + case 2: + func2 (); + break; + case 3: + func3 (); + break; + case 4: + func4 (); + break; + case 5: + func5 (); + break; + } +} + +/* { dg-final { scan-assembler "push(?:l|q)\[ \t\]*\.L\[0-9\]+\\(,%" { target { ! x32 } } } } */ +/* { dg-final { scan-assembler "pushq\[ \t\]%rax" { target x32 } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-not {\t(lfence|pause)} } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c new file mode 100644 index 00000000000..9f6d12d74a1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-8.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */ + +void func0 (void); +void func1 (void); +void func2 (void); +void func3 (void); +void func4 (void); +void func4 (void); +void func5 (void); + +__attribute__ ((indirect_branch("keep"))) +void +bar (int i) +{ + switch (i) + { + default: + func0 (); + break; + case 1: + func1 (); + break; + case 2: + func2 (); + break; + case 3: + func3 (); + break; + case 4: + func4 (); + break; + case 5: + func5 (); + break; + } +} + +/* { dg-final { scan-assembler-not "__x86.indirect_thunk" } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ -- 2.15.1