]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm, cmd/compile, runtime: add -spectre=ret mode
authorRuss Cox <rsc@golang.org>
Fri, 17 Jan 2020 18:54:30 +0000 (13:54 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 13 Mar 2020 19:05:54 +0000 (19:05 +0000)
This commit extends the -spectre flag to cmd/asm and adds
a new Spectre mitigation mode "ret", which enables the use
of retpolines.

Retpolines prevent speculation about the target of an indirect
jump or call and are described in more detail here:
https://support.google.com/faqs/answer/7625886

Change-Id: I4f2cb982fa94e44d91e49bd98974fd125619c93a
Reviewed-on: https://go-review.googlesource.com/c/go/+/222661
Reviewed-by: Keith Randall <khr@golang.org>
13 files changed:
src/cmd/asm/internal/flags/flags.go
src/cmd/asm/main.go
src/cmd/compile/internal/gc/main.go
src/cmd/internal/obj/arm/asm5.go
src/cmd/internal/obj/arm64/asm7.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/mips/asm0.go
src/cmd/internal/obj/riscv/obj.go
src/cmd/internal/obj/s390x/asmz.go
src/cmd/internal/obj/x86/asm6.go
src/runtime/asm_amd64.s
src/runtime/stubs_amd64.go
test/codegen/retpoline.go [new file with mode: 0644]

index fad87b221a876e8289ca8d35130f91f35bff4785..618b08cc36e9298752a64709e0a61c76b4a819bf 100644 (file)
@@ -24,6 +24,8 @@ var (
        AllErrors  = flag.Bool("e", false, "no limit on number of errors reported")
        SymABIs    = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble")
        Newobj     = flag.Bool("newobj", false, "use new object file format")
+
+       Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
 )
 
 var (
index 6b0a609071acaf33836a4b70b25ebae0e92f00f7..21c8bd963a437ee9aca98a403f207e8684d4e1a7 100644 (file)
@@ -41,6 +41,19 @@ func main() {
        ctxt.Flag_dynlink = *flags.Dynlink
        ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
        ctxt.Flag_newobj = *flags.Newobj
+       switch *flags.Spectre {
+       default:
+               log.Printf("unknown setting -spectre=%s", *flags.Spectre)
+               os.Exit(2)
+       case "":
+               // nothing
+       case "index":
+               // known to compiler; ignore here so people can use
+               // the same list with -gcflags=-spectre=LIST and -asmflags=-spectrre=LIST
+       case "all", "ret":
+               ctxt.Retpoline = true
+       }
+
        ctxt.Bso = bufio.NewWriter(os.Stdout)
        defer ctxt.Bso.Flush()
 
index e3a47339bee86b95adc7de4e406c24cae3994e98..81d31c2007c4e089bec9f172474851bed07eccee 100644 (file)
@@ -294,8 +294,11 @@ func Main(archInit func(*Arch)) {
                        // nothing
                case "all":
                        spectreIndex = true
+                       Ctxt.Retpoline = true
                case "index":
                        spectreIndex = true
+               case "ret":
+                       Ctxt.Retpoline = true
                }
        }
 
index bc55dac8784f790e3d22ea7f298c1043d58c3b86..b398de2dac446f952be403caf84fe00ba913d55d 100644 (file)
@@ -379,6 +379,11 @@ func checkSuffix(c *ctxt5, p *obj.Prog, o *Optab) {
 }
 
 func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+       if ctxt.Retpoline {
+               ctxt.Diag("-spectre=ret not supported on arm")
+               ctxt.Retpoline = false // don't keep printing
+       }
+
        var p *obj.Prog
        var op *obj.Prog
 
index b7e5b9fc172e5096e349a9e8ad42c8ea78ca7066..54cb556b998372193b856fdceae1ea2ecd8511ed 100644 (file)
@@ -881,6 +881,11 @@ var prfopfield = []struct {
 }
 
 func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+       if ctxt.Retpoline {
+               ctxt.Diag("-spectre=ret not supported on arm64")
+               ctxt.Retpoline = false // don't keep printing
+       }
+
        p := cursym.Func.Text
        if p == nil || p.Link == nil { // handle external functions and ELF section symbols
                return
index 76c6f261a31f07803adda232b19c62801eb5dbe8..d1cc536a8c1827dc13dc3771ed0f59d2fec5c53e 100644 (file)
@@ -653,6 +653,7 @@ type Link struct {
        Flag_optimize      bool
        Flag_locationlists bool
        Flag_newobj        bool // use new object file format
+       Retpoline          bool // emit use of retpoline stubs for indirect jmp/call
        Bso                *bufio.Writer
        Pathname           string
        hashmu             sync.Mutex       // protects hash, funchash
index c19541522f376e16b1f5ec1dee7b19d04cf01848..5bbcb01444caa15b158af1ad39c61e4736087dac 100644 (file)
@@ -402,6 +402,11 @@ var oprange [ALAST & obj.AMask][]Optab
 var xcmp [C_NCLASS][C_NCLASS]bool
 
 func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+       if ctxt.Retpoline {
+               ctxt.Diag("-spectre=ret not supported on mips")
+               ctxt.Retpoline = false // don't keep printing
+       }
+
        p := cursym.Func.Text
        if p == nil || p.Link == nil { // handle external functions and ELF section symbols
                return
index 1d2c4981104b2a5daeb3ef65e4ae6683f0c2cea3..7add8e31c6996d7ad5cbe90bb62b1f70048ec16f 100644 (file)
@@ -1835,6 +1835,11 @@ func encodingForProg(p *obj.Prog) encoding {
 // assemble emits machine code.
 // It is called at the very end of the assembly process.
 func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+       if ctxt.Retpoline {
+               ctxt.Diag("-spectre=ret not supported on riscv")
+               ctxt.Retpoline = false // don't keep printing
+       }
+
        var symcode []uint32
        for p := cursym.Func.Text; p != nil; p = p.Link {
                switch p.As {
index 1bb79a2edacada2037b913cc62a348bb6815b2c4..ff7cc73fe495887f07c45744f568752b63b727de 100644 (file)
@@ -442,6 +442,11 @@ var oprange [ALAST & obj.AMask][]Optab
 var xcmp [C_NCLASS][C_NCLASS]bool
 
 func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
+       if ctxt.Retpoline {
+               ctxt.Diag("-spectre=ret not supported on s390x")
+               ctxt.Retpoline = false // don't keep printing
+       }
+
        p := cursym.Func.Text
        if p == nil || p.Link == nil { // handle external functions and ELF section symbols
                return
index cfeb179a863992c461982c3006d3f092e26ce8cf..5597aab0198f301f0823278c07ce2e81b15baea1 100644 (file)
@@ -1875,6 +1875,17 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
                                p.As = spadjop(ctxt, ASUBL, ASUBQ)
                        }
                }
+               if ctxt.Retpoline && (p.As == obj.ACALL || p.As == obj.AJMP) && (p.To.Type == obj.TYPE_REG || p.To.Type == obj.TYPE_MEM) {
+                       if p.To.Type != obj.TYPE_REG {
+                               ctxt.Diag("non-retpoline-compatible: %v", p)
+                               continue
+                       }
+                       p.To.Type = obj.TYPE_BRANCH
+                       p.To.Name = obj.NAME_EXTERN
+                       p.To.Sym = ctxt.Lookup("runtime.retpoline" + obj.Rconv(int(p.To.Reg)))
+                       p.To.Reg = 0
+                       p.To.Offset = 0
+               }
        }
 
        var count int64 // rough count of number of instructions
index c191599c28c58dfae0809ed68ab88584583a8fe8..b872b8834d782320ede94f7990a8b3c9db939cba 100644 (file)
@@ -510,7 +510,8 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32;                \
        /* call function */                     \
        MOVQ    f+8(FP), DX;                    \
        PCDATA  $PCDATA_StackMapIndex, $0;      \
-       CALL    (DX);                           \
+       MOVQ    (DX), AX;                       \
+       CALL    AX;                             \
        /* copy return values back */           \
        MOVQ    argtype+0(FP), DX;              \
        MOVQ    argptr+16(FP), DI;              \
@@ -1743,3 +1744,34 @@ TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
 DATA runtime·tls_g+0(SB)/8, $16
 GLOBL runtime·tls_g+0(SB), NOPTR, $8
 #endif
+
+// The compiler and assembler's -spectre=ret mode rewrites
+// all indirect CALL AX / JMP AX instructions to be
+// CALL retpolineAX / JMP retpolineAX.
+// See https://support.google.com/faqs/answer/7625886.
+#define RETPOLINE(reg) \
+       /*   CALL setup */     BYTE $0xE8; BYTE $(2+2); BYTE $0; BYTE $0; BYTE $0;      \
+       /* nospec: */                                                                   \
+       /*   PAUSE */           BYTE $0xF3; BYTE $0x90;                                 \
+       /*   JMP nospec */      BYTE $0xEB; BYTE $-(2+2);                               \
+       /* setup: */                                                                    \
+       /*   MOVQ AX, 0(SP) */  BYTE $0x48|((reg&8)>>1); BYTE $0x89;                    \
+                               BYTE $0x04|((reg&7)<<3); BYTE $0x24;                    \
+       /*   RET */             BYTE $0xC3
+
+TEXT runtime·retpolineAX(SB),NOSPLIT,$0; RETPOLINE(0)
+TEXT runtime·retpolineCX(SB),NOSPLIT,$0; RETPOLINE(1)
+TEXT runtime·retpolineDX(SB),NOSPLIT,$0; RETPOLINE(2)
+TEXT runtime·retpolineBX(SB),NOSPLIT,$0; RETPOLINE(3)
+/* SP is 4, can't happen / magic encodings */
+TEXT runtime·retpolineBP(SB),NOSPLIT,$0; RETPOLINE(5)
+TEXT runtime·retpolineSI(SB),NOSPLIT,$0; RETPOLINE(6)
+TEXT runtime·retpolineDI(SB),NOSPLIT,$0; RETPOLINE(7)
+TEXT runtime·retpolineR8(SB),NOSPLIT,$0; RETPOLINE(8)
+TEXT runtime·retpolineR9(SB),NOSPLIT,$0; RETPOLINE(9)
+TEXT runtime·retpolineR10(SB),NOSPLIT,$0; RETPOLINE(10)
+TEXT runtime·retpolineR11(SB),NOSPLIT,$0; RETPOLINE(11)
+TEXT runtime·retpolineR12(SB),NOSPLIT,$0; RETPOLINE(12)
+TEXT runtime·retpolineR13(SB),NOSPLIT,$0; RETPOLINE(13)
+TEXT runtime·retpolineR14(SB),NOSPLIT,$0; RETPOLINE(14)
+TEXT runtime·retpolineR15(SB),NOSPLIT,$0; RETPOLINE(15)
index b4c0df115324cb24e2d10e2d27b9e2725d11bd3e..5b79d66762ad57603972f031db3cab25a8941f01 100644 (file)
@@ -9,3 +9,20 @@ func stackcheck()
 
 // Called from assembly only; declared for go vet.
 func settls() // argument in DI
+
+// Retpolines, used by -spectre=ret flag in cmd/asm, cmd/compile.
+func retpolineAX()
+func retpolineCX()
+func retpolineDX()
+func retpolineBX()
+func retpolineBP()
+func retpolineSI()
+func retpolineDI()
+func retpolineR8()
+func retpolineR9()
+func retpolineR10()
+func retpolineR11()
+func retpolineR12()
+func retpolineR13()
+func retpolineR14()
+func retpolineR15()
diff --git a/test/codegen/retpoline.go b/test/codegen/retpoline.go
new file mode 100644 (file)
index 0000000..15d6a26
--- /dev/null
@@ -0,0 +1,14 @@
+// +build amd64
+// asmcheck -gcflags=-spectre=ret
+
+package codegen
+
+func CallFunc(f func()) {
+       // amd64:`CALL\truntime.retpoline`
+       f()
+}
+
+func CallInterface(x interface{ M() }) {
+       // amd64:`CALL\truntime.retpoline`
+       x.M()
+}