Skip to content

Commit 92bda33

Browse files
committed
runtime: revert signal stack mlocking
Go 1.14 included a (rather awful) workaround for a Linux kernel bug that corrupted vector registers on x86 CPUs during signal delivery (https://bugzilla.kernel.org/show_bug.cgi?id=205663). This bug was introduced in Linux 5.2 and fixed in 5.3.15, 5.4.2 and all 5.5 and later kernels. The fix was also back-ported by major distros. This workaround was necessary, but had unfortunate downsides, including causing Go programs to exceed the mlock ulimit in many configurations (#37436). We're reasonably confident that by the Go 1.16 release, the number of systems running affected kernels will be vanishingly small. Hence, this CL removes this workaround. This effectively reverts CLs 209597 (version parser), 209899 (mlock top of signal stack), 210299 (better failure message), 223121 (soft mlock failure handling), and 244059 (special-case patched Ubuntu kernels). The one thing we keep is the osArchInit function. It's empty everywhere now, but is a reasonable hook to have. Updates #35326, #35777 (the original register corruption bugs). Updates #40184 (request to revert in 1.15). Fixes #35979. Change-Id: Ie213270837095576f1f3ef46bf3de187dc486c50 Reviewed-on: https://go-review.googlesource.com/c/go/+/246200 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent b235317 commit 92bda33

File tree

10 files changed

+1
-263
lines changed

10 files changed

+1
-263
lines changed

src/runtime/defs_linux_386.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,3 @@ type sockaddr_un struct {
226226
family uint16
227227
path [108]byte
228228
}
229-
230-
const __NEW_UTS_LEN = 64
231-
232-
type new_utsname struct {
233-
sysname [__NEW_UTS_LEN + 1]byte
234-
nodename [__NEW_UTS_LEN + 1]byte
235-
release [__NEW_UTS_LEN + 1]byte
236-
version [__NEW_UTS_LEN + 1]byte
237-
machine [__NEW_UTS_LEN + 1]byte
238-
domainname [__NEW_UTS_LEN + 1]byte
239-
}

src/runtime/defs_linux_amd64.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,3 @@ type sockaddr_un struct {
262262
family uint16
263263
path [108]byte
264264
}
265-
266-
const __NEW_UTS_LEN = 64
267-
268-
type new_utsname struct {
269-
sysname [__NEW_UTS_LEN + 1]byte
270-
nodename [__NEW_UTS_LEN + 1]byte
271-
release [__NEW_UTS_LEN + 1]byte
272-
version [__NEW_UTS_LEN + 1]byte
273-
machine [__NEW_UTS_LEN + 1]byte
274-
domainname [__NEW_UTS_LEN + 1]byte
275-
}

src/runtime/export_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ var PhysHugePageSize = physHugePageSize
4343

4444
var NetpollGenericInit = netpollGenericInit
4545

46-
var ParseRelease = parseRelease
47-
4846
var Memmove = memmove
4947
var MemclrNoHeapPointers = memclrNoHeapPointers
5048

src/runtime/os_linux.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,20 +328,11 @@ func libpreinit() {
328328
initsig(true)
329329
}
330330

331-
// gsignalInitQuirk, if non-nil, is called for every allocated gsignal G.
332-
//
333-
// TODO(austin): Remove this after Go 1.15 when we remove the
334-
// mlockGsignal workaround.
335-
var gsignalInitQuirk func(gsignal *g)
336-
337331
// Called to initialize a new m (including the bootstrap m).
338332
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
339333
func mpreinit(mp *m) {
340334
mp.gsignal = malg(32 * 1024) // Linux wants >= 2K
341335
mp.gsignal.m = mp
342-
if gsignalInitQuirk != nil {
343-
gsignalInitQuirk(mp.gsignal)
344-
}
345336
}
346337

347338
func gettid() uint32

src/runtime/os_linux_x86.go

Lines changed: 1 addition & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -7,120 +7,4 @@
77

88
package runtime
99

10-
import (
11-
"runtime/internal/atomic"
12-
"unsafe"
13-
)
14-
15-
//go:noescape
16-
func uname(utsname *new_utsname) int
17-
18-
func mlock(addr, len uintptr) int
19-
20-
func osArchInit() {
21-
// Linux 5.2 introduced a bug that can corrupt vector
22-
// registers on return from a signal if the signal stack isn't
23-
// faulted in:
24-
// https://bugzilla.kernel.org/show_bug.cgi?id=205663
25-
//
26-
// It was fixed in 5.3.15, 5.4.2, and all 5.5 and later
27-
// kernels.
28-
//
29-
// If we're on an affected kernel, work around this issue by
30-
// mlocking the top page of every signal stack. This doesn't
31-
// help for signal stacks created in C, but there's not much
32-
// we can do about that.
33-
//
34-
// TODO(austin): Remove this in Go 1.15, at which point it
35-
// will be unlikely to encounter any of the affected kernels
36-
// in the wild.
37-
38-
var uts new_utsname
39-
if uname(&uts) < 0 {
40-
throw("uname failed")
41-
}
42-
// Check for null terminator to ensure gostringnocopy doesn't
43-
// walk off the end of the release string.
44-
found := false
45-
for _, b := range uts.release {
46-
if b == 0 {
47-
found = true
48-
break
49-
}
50-
}
51-
if !found {
52-
return
53-
}
54-
rel := gostringnocopy(&uts.release[0])
55-
56-
major, minor, patch, ok := parseRelease(rel)
57-
if !ok {
58-
return
59-
}
60-
61-
if major == 5 && minor == 4 && patch < 2 {
62-
// All 5.4 versions of Ubuntu are patched.
63-
procVersion := []byte("/proc/version\000")
64-
f := open(&procVersion[0], _O_RDONLY, 0)
65-
if f >= 0 {
66-
var buf [512]byte
67-
p := noescape(unsafe.Pointer(&buf[0]))
68-
n := read(f, p, int32(len(buf)))
69-
closefd(f)
70-
71-
needle := []byte("Ubuntu")
72-
contains:
73-
for i, c := range buf[:n] {
74-
if c != needle[0] {
75-
continue
76-
}
77-
if int(n)-i < len(needle) {
78-
break
79-
}
80-
for j, c2 := range needle {
81-
if c2 != buf[i+j] {
82-
continue contains
83-
}
84-
}
85-
// This is an Ubuntu system.
86-
return
87-
}
88-
}
89-
}
90-
91-
if major == 5 && (minor == 2 || minor == 3 && patch < 15 || minor == 4 && patch < 2) {
92-
gsignalInitQuirk = mlockGsignal
93-
if m0.gsignal != nil {
94-
throw("gsignal quirk too late")
95-
}
96-
throwReportQuirk = throwBadKernel
97-
}
98-
}
99-
100-
func mlockGsignal(gsignal *g) {
101-
if atomic.Load(&touchStackBeforeSignal) != 0 {
102-
// mlock has already failed, don't try again.
103-
return
104-
}
105-
106-
// This mlock call may fail, but we don't report the failure.
107-
// Instead, if something goes badly wrong, we rely on prepareSignalM
108-
// and throwBadKernel to do further mitigation and to report a problem
109-
// to the user if mitigation fails. This is because many
110-
// systems have a limit on the total mlock size, and many kernels
111-
// that appear to have bad versions are actually patched to avoid the
112-
// bug described above. We want Go 1.14 to run on those systems.
113-
// See #37436.
114-
if errno := mlock(gsignal.stack.hi-physPageSize, physPageSize); errno < 0 {
115-
atomic.Store(&touchStackBeforeSignal, uint32(-errno))
116-
}
117-
}
118-
119-
// throwBadKernel is called, via throwReportQuirk, by throw.
120-
func throwBadKernel() {
121-
if errno := atomic.Load(&touchStackBeforeSignal); errno != 0 {
122-
println("runtime: note: your Linux kernel may be buggy")
123-
println("runtime: note: see https://golang.org/wiki/LinuxKernelSignalVectorBug")
124-
println("runtime: note: mlock workaround for kernel bug failed with errno", errno)
125-
}
126-
}
10+
func osArchInit() {}

src/runtime/panic.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,12 +1283,6 @@ func startpanic_m() bool {
12831283
}
12841284
}
12851285

1286-
// throwReportQuirk, if non-nil, is called by throw after dumping the stacks.
1287-
//
1288-
// TODO(austin): Remove this after Go 1.15 when we remove the
1289-
// mlockGsignal workaround.
1290-
var throwReportQuirk func()
1291-
12921286
var didothers bool
12931287
var deadlock mutex
12941288

@@ -1335,10 +1329,6 @@ func dopanic_m(gp *g, pc, sp uintptr) bool {
13351329

13361330
printDebugLog()
13371331

1338-
if throwReportQuirk != nil {
1339-
throwReportQuirk()
1340-
}
1341-
13421332
return docrash
13431333
}
13441334

src/runtime/string.go

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -499,37 +499,3 @@ func gostringw(strw *uint16) string {
499499
b[n2] = 0 // for luck
500500
return s[:n2]
501501
}
502-
503-
// parseRelease parses a dot-separated version number. It follows the
504-
// semver syntax, but allows the minor and patch versions to be
505-
// elided.
506-
func parseRelease(rel string) (major, minor, patch int, ok bool) {
507-
// Strip anything after a dash or plus.
508-
for i := 0; i < len(rel); i++ {
509-
if rel[i] == '-' || rel[i] == '+' {
510-
rel = rel[:i]
511-
break
512-
}
513-
}
514-
515-
next := func() (int, bool) {
516-
for i := 0; i < len(rel); i++ {
517-
if rel[i] == '.' {
518-
ver, ok := atoi(rel[:i])
519-
rel = rel[i+1:]
520-
return ver, ok
521-
}
522-
}
523-
ver, ok := atoi(rel)
524-
rel = ""
525-
return ver, ok
526-
}
527-
if major, ok = next(); !ok || rel == "" {
528-
return
529-
}
530-
if minor, ok = next(); !ok || rel == "" {
531-
return
532-
}
533-
patch, ok = next()
534-
return
535-
}

src/runtime/string_test.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -454,34 +454,3 @@ func TestAtoi32(t *testing.T) {
454454
}
455455
}
456456
}
457-
458-
type parseReleaseTest struct {
459-
in string
460-
major, minor, patch int
461-
}
462-
463-
var parseReleaseTests = []parseReleaseTest{
464-
{"", -1, -1, -1},
465-
{"x", -1, -1, -1},
466-
{"5", 5, 0, 0},
467-
{"5.12", 5, 12, 0},
468-
{"5.12-x", 5, 12, 0},
469-
{"5.12.1", 5, 12, 1},
470-
{"5.12.1-x", 5, 12, 1},
471-
{"5.12.1.0", 5, 12, 1},
472-
{"5.20496382327982653440", -1, -1, -1},
473-
}
474-
475-
func TestParseRelease(t *testing.T) {
476-
for _, test := range parseReleaseTests {
477-
major, minor, patch, ok := runtime.ParseRelease(test.in)
478-
if !ok {
479-
major, minor, patch = -1, -1, -1
480-
}
481-
if test.major != major || test.minor != minor || test.patch != patch {
482-
t.Errorf("parseRelease(%q) = (%v, %v, %v) want (%v, %v, %v)",
483-
test.in, major, minor, patch,
484-
test.major, test.minor, test.patch)
485-
}
486-
}
487-
}

src/runtime/sys_linux_386.s

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
#define SYS_socketcall 102
4040
#define SYS_setittimer 104
4141
#define SYS_clone 120
42-
#define SYS_uname 122
43-
#define SYS_mlock 150
4442
#define SYS_sched_yield 158
4543
#define SYS_nanosleep 162
4644
#define SYS_rt_sigreturn 173
@@ -808,20 +806,3 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-4
808806
INVOKE_SYSCALL
809807
MOVL AX, ret+0(FP)
810808
RET
811-
812-
// func uname(utsname *new_utsname) int
813-
TEXT ·uname(SB),NOSPLIT,$0-8
814-
MOVL $SYS_uname, AX
815-
MOVL utsname+0(FP), BX
816-
INVOKE_SYSCALL
817-
MOVL AX, ret+4(FP)
818-
RET
819-
820-
// func mlock(addr, len uintptr) int
821-
TEXT ·mlock(SB),NOSPLIT,$0-12
822-
MOVL $SYS_mlock, AX
823-
MOVL addr+0(FP), BX
824-
MOVL len+4(FP), CX
825-
INVOKE_SYSCALL
826-
MOVL AX, ret+8(FP)
827-
RET

src/runtime/sys_linux_amd64.s

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@
3333
#define SYS_clone 56
3434
#define SYS_exit 60
3535
#define SYS_kill 62
36-
#define SYS_uname 63
3736
#define SYS_fcntl 72
3837
#define SYS_sigaltstack 131
39-
#define SYS_mlock 149
4038
#define SYS_arch_prctl 158
4139
#define SYS_gettid 186
4240
#define SYS_futex 202
@@ -789,20 +787,3 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-8
789787
SYSCALL
790788
MOVQ AX, ret+0(FP)
791789
RET
792-
793-
// func uname(utsname *new_utsname) int
794-
TEXT ·uname(SB),NOSPLIT,$0-16
795-
MOVQ utsname+0(FP), DI
796-
MOVL $SYS_uname, AX
797-
SYSCALL
798-
MOVQ AX, ret+8(FP)
799-
RET
800-
801-
// func mlock(addr, len uintptr) int
802-
TEXT ·mlock(SB),NOSPLIT,$0-24
803-
MOVQ addr+0(FP), DI
804-
MOVQ len+8(FP), SI
805-
MOVL $SYS_mlock, AX
806-
SYSCALL
807-
MOVQ AX, ret+16(FP)
808-
RET

0 commit comments

Comments
 (0)