From 8572b1cfea49b6108b9fb8ea650c4999ba70d381 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Tue, 3 Feb 2026 15:45:21 +0000 Subject: [PATCH] internal/runtime: fix assembly for spectre retpoline instrumentation In the last year we added two CALLs whose targets are loaded from memory. Change them to call from a register so that the instrumentation for spectre mitigations works. This change also adds a smoke test for the spectre build flags. For #77420. Change-Id: I35ec723449ff6a712bcce3276bf1df3fa932bddc Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/741541 LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Pratt Reviewed-by: Dmitri Shuralyov --- src/cmd/dist/test.go | 20 +++++++++++++++++-- src/internal/runtime/gc/scan/scan_amd64.s | 6 ++++-- .../runtime/startlinetest/func_amd64.s | 3 ++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 48c3aa5efd..4984f930b9 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -789,7 +789,7 @@ func (t *tester) registerTests() { if !t.compileOnly && !t.short { t.registerTest("GODEBUG=gcstoptheworld=2 archive/zip", &goTest{ - variant: "runtime:gcstoptheworld2", + variant: "gcstoptheworld2", timeout: 300 * time.Second, short: true, env: []string{"GODEBUG=gcstoptheworld=2"}, @@ -797,7 +797,7 @@ func (t *tester) registerTests() { }) t.registerTest("GODEBUG=gccheckmark=1 runtime", &goTest{ - variant: "runtime:gccheckmark", + variant: "gccheckmark", timeout: 300 * time.Second, short: true, env: []string{"GODEBUG=gccheckmark=1"}, @@ -805,6 +805,22 @@ func (t *tester) registerTests() { }) } + // Spectre mitigation smoke test. + if goos == "linux" && goarch == "amd64" { + // Pick a bunch of packages known to have some assembly. + pkgs := []string{"internal/runtime/...", "reflect", "crypto/..."} + if !t.short { + pkgs = append(pkgs, "runtime") + } + t.registerTest("spectre", + &goTest{ + variant: "spectre", + short: true, + env: []string{"GOFLAGS=-gcflags=all=-spectre=all -asmflags=all=-spectre=all"}, + pkgs: pkgs, + }) + } + // morestack tests. We only run these in long-test mode // (with GO_TEST_SHORT=0) because the runtime test is // already quite long and mayMoreStackMove makes it about diff --git a/src/internal/runtime/gc/scan/scan_amd64.s b/src/internal/runtime/gc/scan/scan_amd64.s index 9b4950a767..24354a3a1c 100644 --- a/src/internal/runtime/gc/scan/scan_amd64.s +++ b/src/internal/runtime/gc/scan/scan_amd64.s @@ -12,7 +12,8 @@ TEXT ·ExpandAVX512(SB), NOSPLIT, $0-24 // Call the expander for this size class LEAQ ·gcExpandersAVX512(SB), BX - CALL (BX)(CX*8) + MOVQ (BX)(CX*8), DX // Move to register first so -spectre works + CALL DX MOVQ unpacked+16(FP), DI // Expanded output bitmap pointer VMOVDQU64 Z1, 0(DI) @@ -25,7 +26,8 @@ TEXT ·scanSpanPackedAVX512(SB), NOSPLIT, $256-44 MOVQ objMarks+16(FP), AX MOVQ sizeClass+24(FP), CX LEAQ ·gcExpandersAVX512(SB), BX - CALL (BX)(CX*8) + MOVQ (BX)(CX*8), DX // Move to register first so -spectre works + CALL DX // Z3+Z4 = Load the pointer mask MOVQ ptrMask+32(FP), AX diff --git a/src/internal/runtime/startlinetest/func_amd64.s b/src/internal/runtime/startlinetest/func_amd64.s index 96982bedab..e60ba7af3f 100644 --- a/src/internal/runtime/startlinetest/func_amd64.s +++ b/src/internal/runtime/startlinetest/func_amd64.s @@ -24,5 +24,6 @@ TEXT ·AsmFunc(SB),NOSPLIT,$8-0 NO_LOCAL_POINTERS MOVQ $0, AX // wantInlined MOVQ ·CallerStartLine(SB), DX - CALL (DX) + MOVQ (DX), DX // Move to a register first for -spectre + CALL DX RET -- 2.52.0