mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
cmd/asm, cmd/compile, runtime: add -spectre=ret mode
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>
This commit is contained in:
parent
877ef86bec
commit
fc8a6336d1
|
@ -24,6 +24,8 @@ var (
|
||||||
AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
|
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")
|
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")
|
Newobj = flag.Bool("newobj", false, "use new object file format")
|
||||||
|
|
||||||
|
Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -41,6 +41,19 @@ func main() {
|
||||||
ctxt.Flag_dynlink = *flags.Dynlink
|
ctxt.Flag_dynlink = *flags.Dynlink
|
||||||
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
|
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
|
||||||
ctxt.Flag_newobj = *flags.Newobj
|
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)
|
ctxt.Bso = bufio.NewWriter(os.Stdout)
|
||||||
defer ctxt.Bso.Flush()
|
defer ctxt.Bso.Flush()
|
||||||
|
|
||||||
|
|
|
@ -294,8 +294,11 @@ func Main(archInit func(*Arch)) {
|
||||||
// nothing
|
// nothing
|
||||||
case "all":
|
case "all":
|
||||||
spectreIndex = true
|
spectreIndex = true
|
||||||
|
Ctxt.Retpoline = true
|
||||||
case "index":
|
case "index":
|
||||||
spectreIndex = true
|
spectreIndex = true
|
||||||
|
case "ret":
|
||||||
|
Ctxt.Retpoline = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -379,6 +379,11 @@ func checkSuffix(c *ctxt5, p *obj.Prog, o *Optab) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
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 p *obj.Prog
|
||||||
var op *obj.Prog
|
var op *obj.Prog
|
||||||
|
|
||||||
|
|
|
@ -881,6 +881,11 @@ var prfopfield = []struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
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
|
p := cursym.Func.Text
|
||||||
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
||||||
return
|
return
|
||||||
|
|
|
@ -653,6 +653,7 @@ type Link struct {
|
||||||
Flag_optimize bool
|
Flag_optimize bool
|
||||||
Flag_locationlists bool
|
Flag_locationlists bool
|
||||||
Flag_newobj bool // use new object file format
|
Flag_newobj bool // use new object file format
|
||||||
|
Retpoline bool // emit use of retpoline stubs for indirect jmp/call
|
||||||
Bso *bufio.Writer
|
Bso *bufio.Writer
|
||||||
Pathname string
|
Pathname string
|
||||||
hashmu sync.Mutex // protects hash, funchash
|
hashmu sync.Mutex // protects hash, funchash
|
||||||
|
|
|
@ -402,6 +402,11 @@ var oprange [ALAST & obj.AMask][]Optab
|
||||||
var xcmp [C_NCLASS][C_NCLASS]bool
|
var xcmp [C_NCLASS][C_NCLASS]bool
|
||||||
|
|
||||||
func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
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
|
p := cursym.Func.Text
|
||||||
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
||||||
return
|
return
|
||||||
|
|
|
@ -1835,6 +1835,11 @@ func encodingForProg(p *obj.Prog) encoding {
|
||||||
// assemble emits machine code.
|
// assemble emits machine code.
|
||||||
// It is called at the very end of the assembly process.
|
// It is called at the very end of the assembly process.
|
||||||
func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
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
|
var symcode []uint32
|
||||||
for p := cursym.Func.Text; p != nil; p = p.Link {
|
for p := cursym.Func.Text; p != nil; p = p.Link {
|
||||||
switch p.As {
|
switch p.As {
|
||||||
|
|
|
@ -442,6 +442,11 @@ var oprange [ALAST & obj.AMask][]Optab
|
||||||
var xcmp [C_NCLASS][C_NCLASS]bool
|
var xcmp [C_NCLASS][C_NCLASS]bool
|
||||||
|
|
||||||
func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
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
|
p := cursym.Func.Text
|
||||||
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
if p == nil || p.Link == nil { // handle external functions and ELF section symbols
|
||||||
return
|
return
|
||||||
|
|
|
@ -1875,6 +1875,17 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
p.As = spadjop(ctxt, ASUBL, ASUBQ)
|
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
|
var count int64 // rough count of number of instructions
|
||||||
|
|
|
@ -510,7 +510,8 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32; \
|
||||||
/* call function */ \
|
/* call function */ \
|
||||||
MOVQ f+8(FP), DX; \
|
MOVQ f+8(FP), DX; \
|
||||||
PCDATA $PCDATA_StackMapIndex, $0; \
|
PCDATA $PCDATA_StackMapIndex, $0; \
|
||||||
CALL (DX); \
|
MOVQ (DX), AX; \
|
||||||
|
CALL AX; \
|
||||||
/* copy return values back */ \
|
/* copy return values back */ \
|
||||||
MOVQ argtype+0(FP), DX; \
|
MOVQ argtype+0(FP), DX; \
|
||||||
MOVQ argptr+16(FP), DI; \
|
MOVQ argptr+16(FP), DI; \
|
||||||
|
@ -1743,3 +1744,34 @@ TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
|
||||||
DATA runtime·tls_g+0(SB)/8, $16
|
DATA runtime·tls_g+0(SB)/8, $16
|
||||||
GLOBL runtime·tls_g+0(SB), NOPTR, $8
|
GLOBL runtime·tls_g+0(SB), NOPTR, $8
|
||||||
#endif
|
#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)
|
||||||
|
|
|
@ -9,3 +9,20 @@ func stackcheck()
|
||||||
|
|
||||||
// Called from assembly only; declared for go vet.
|
// Called from assembly only; declared for go vet.
|
||||||
func settls() // argument in DI
|
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()
|
||||||
|
|
14
test/codegen/retpoline.go
Normal file
14
test/codegen/retpoline.go
Normal file
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in a new issue