Skip to content

Commit 8174f7f

Browse files
committed
runtime: mlock top of signal stack on Linux 5.2–5.4.1
Linux 5.2 introduced a bug that can corrupt vector registers on return from a signal if the signal stack isn't faulted in: https://bugzilla.kernel.org/show_bug.cgi?id=205663 This CL works around this by mlocking the top page of all Go signal stacks on the affected kernels. Fixes #35326, #35777 Change-Id: I77c80a2baa4780827633f92f464486caa222295d Reviewed-on: https://go-review.googlesource.com/c/go/+/209899 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent fa3a121 commit 8174f7f

11 files changed

+122
-0
lines changed

src/runtime/defs_linux_amd64.go

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

src/runtime/os_linux.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ func getHugePageSize() uintptr {
289289
func osinit() {
290290
ncpu = getproccount()
291291
physHugePageSize = getHugePageSize()
292+
osArchInit()
292293
}
293294

294295
var urandom_dev = []byte("/dev/urandom\x00")
@@ -318,11 +319,20 @@ func libpreinit() {
318319
initsig(true)
319320
}
320321

322+
// gsignalInitQuirk, if non-nil, is called for every allocated gsignal G.
323+
//
324+
// TODO(austin): Remove this after Go 1.15 when we remove the
325+
// mlockGsignal workaround.
326+
var gsignalInitQuirk func(gsignal *g)
327+
321328
// Called to initialize a new m (including the bootstrap m).
322329
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
323330
func mpreinit(mp *m) {
324331
mp.gsignal = malg(32 * 1024) // Linux wants >= 2K
325332
mp.gsignal.m = mp
333+
if gsignalInitQuirk != nil {
334+
gsignalInitQuirk(mp.gsignal)
335+
}
326336
}
327337

328338
func gettid() uint32

src/runtime/os_linux_386.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package runtime
6+
7+
func osArchInit() {}

src/runtime/os_linux_amd64.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package runtime
6+
7+
//go:noescape
8+
func uname(utsname *new_utsname) int
9+
10+
func mlock(addr, len uintptr) int
11+
12+
func osArchInit() {
13+
// Linux 5.2 introduced a bug that can corrupt vector
14+
// registers on return from a signal if the signal stack isn't
15+
// faulted in:
16+
// https://bugzilla.kernel.org/show_bug.cgi?id=205663
17+
//
18+
// It was fixed in 5.3.15, 5.4.2, and all 5.5 and later
19+
// kernels.
20+
//
21+
// If we're on an affected kernel, work around this issue by
22+
// mlocking the top page of every signal stack. This doesn't
23+
// help for signal stacks created in C, but there's not much
24+
// we can do about that.
25+
//
26+
// TODO(austin): Remove this in Go 1.15, at which point it
27+
// will be unlikely to encounter any of the affected kernels
28+
// in the wild.
29+
30+
var uts new_utsname
31+
if uname(&uts) < 0 {
32+
throw("uname failed")
33+
}
34+
// Check for null terminator to ensure gostringnocopy doesn't
35+
// walk off the end of the release string.
36+
found := false
37+
for _, b := range uts.release {
38+
if b == 0 {
39+
found = true
40+
break
41+
}
42+
}
43+
if !found {
44+
return
45+
}
46+
rel := gostringnocopy(&uts.release[0])
47+
48+
major, minor, patch, ok := parseRelease(rel)
49+
if !ok {
50+
return
51+
}
52+
53+
if major == 5 && (minor == 2 || minor == 3 && patch < 15 || minor == 4 && patch < 2) {
54+
gsignalInitQuirk = mlockGsignal
55+
if m0.gsignal != nil {
56+
throw("gsignal quirk too late")
57+
}
58+
}
59+
}
60+
61+
func mlockGsignal(gsignal *g) {
62+
mlock(gsignal.stack.hi-physPageSize, physPageSize)
63+
}

src/runtime/os_linux_arm.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func archauxv(tag, val uintptr) {
3939
}
4040
}
4141

42+
func osArchInit() {}
43+
4244
//go:nosplit
4345
func cputicks() int64 {
4446
// Currently cputicks() is used in blocking profiler and to seed fastrand().

src/runtime/os_linux_arm64.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ func archauxv(tag, val uintptr) {
2727
}
2828
}
2929

30+
func osArchInit() {}
31+
3032
//go:nosplit
3133
func cputicks() int64 {
3234
// Currently cputicks() is used in blocking profiler and to seed fastrand().

src/runtime/os_linux_mips64x.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ package runtime
1010
func archauxv(tag, val uintptr) {
1111
}
1212

13+
func osArchInit() {}
14+
1315
//go:nosplit
1416
func cputicks() int64 {
1517
// Currently cputicks() is used in blocking profiler and to seed fastrand().

src/runtime/os_linux_mipsx.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ package runtime
1010
func archauxv(tag, val uintptr) {
1111
}
1212

13+
func osArchInit() {}
14+
1315
//go:nosplit
1416
func cputicks() int64 {
1517
// Currently cputicks() is used in blocking profiler and to seed fastrand().

src/runtime/os_linux_ppc64x.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ func archauxv(tag, val uintptr) {
2020
cpu.HWCap2 = uint(val)
2121
}
2222
}
23+
24+
func osArchInit() {}

src/runtime/os_linux_s390x.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ func archauxv(tag, val uintptr) {
1717
cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0
1818
}
1919
}
20+
21+
func osArchInit() {}

src/runtime/sys_linux_amd64.s

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
#define SYS_clone 56
3434
#define SYS_exit 60
3535
#define SYS_kill 62
36+
#define SYS_uname 63
3637
#define SYS_fcntl 72
3738
#define SYS_sigaltstack 131
39+
#define SYS_mlock 149
3840
#define SYS_arch_prctl 158
3941
#define SYS_gettid 186
4042
#define SYS_futex 202
@@ -764,3 +766,20 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-8
764766
SYSCALL
765767
MOVQ AX, ret+0(FP)
766768
RET
769+
770+
// func uname(utsname *new_utsname) int
771+
TEXT ·uname(SB),NOSPLIT,$0-16
772+
MOVQ utsname+0(FP), DI
773+
MOVL $SYS_uname, AX
774+
SYSCALL
775+
MOVQ AX, ret+8(FP)
776+
RET
777+
778+
// func mlock(addr, len uintptr) int
779+
TEXT ·mlock(SB),NOSPLIT,$0-24
780+
MOVQ addr+0(FP), DI
781+
MOVQ len+8(FP), SI
782+
MOVL $SYS_mlock, AX
783+
SYSCALL
784+
MOVQ AX, ret+16(FP)
785+
RET

0 commit comments

Comments
 (0)