]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: mlock top of signal stack on Linux 5.2–5.4.1
authorAustin Clements <austin@google.com>
Mon, 2 Dec 2019 22:36:25 +0000 (17:36 -0500)
committerAustin Clements <austin@google.com>
Thu, 5 Dec 2019 01:48:14 +0000 (01:48 +0000)
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 <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/runtime/defs_linux_amd64.go
src/runtime/os_linux.go
src/runtime/os_linux_386.go [new file with mode: 0644]
src/runtime/os_linux_amd64.go [new file with mode: 0644]
src/runtime/os_linux_arm.go
src/runtime/os_linux_arm64.go
src/runtime/os_linux_mips64x.go
src/runtime/os_linux_mipsx.go
src/runtime/os_linux_ppc64x.go
src/runtime/os_linux_s390x.go
src/runtime/sys_linux_amd64.s

index 9eb5646ca32d239a390a8a18bc5d3f64b14173bb..8144354d5af40561df6eefccccd095627ebc24d9 100644 (file)
@@ -263,3 +263,14 @@ type sockaddr_un struct {
        family uint16
        path   [108]byte
 }
+
+const __NEW_UTS_LEN = 64
+
+type new_utsname struct {
+       sysname    [__NEW_UTS_LEN + 1]byte
+       nodename   [__NEW_UTS_LEN + 1]byte
+       release    [__NEW_UTS_LEN + 1]byte
+       version    [__NEW_UTS_LEN + 1]byte
+       machine    [__NEW_UTS_LEN + 1]byte
+       domainname [__NEW_UTS_LEN + 1]byte
+}
index 27c66f7449ac8291d60338bafadc7f749cd39748..1eb86e9c8b9bb396f379a7447e571668655a8a51 100644 (file)
@@ -289,6 +289,7 @@ func getHugePageSize() uintptr {
 func osinit() {
        ncpu = getproccount()
        physHugePageSize = getHugePageSize()
+       osArchInit()
 }
 
 var urandom_dev = []byte("/dev/urandom\x00")
@@ -318,11 +319,20 @@ func libpreinit() {
        initsig(true)
 }
 
+// gsignalInitQuirk, if non-nil, is called for every allocated gsignal G.
+//
+// TODO(austin): Remove this after Go 1.15 when we remove the
+// mlockGsignal workaround.
+var gsignalInitQuirk func(gsignal *g)
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the parent thread (main thread in case of bootstrap), can allocate memory.
 func mpreinit(mp *m) {
        mp.gsignal = malg(32 * 1024) // Linux wants >= 2K
        mp.gsignal.m = mp
+       if gsignalInitQuirk != nil {
+               gsignalInitQuirk(mp.gsignal)
+       }
 }
 
 func gettid() uint32
diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go
new file mode 100644 (file)
index 0000000..9be88a5
--- /dev/null
@@ -0,0 +1,7 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+func osArchInit() {}
diff --git a/src/runtime/os_linux_amd64.go b/src/runtime/os_linux_amd64.go
new file mode 100644 (file)
index 0000000..21e4790
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+//go:noescape
+func uname(utsname *new_utsname) int
+
+func mlock(addr, len uintptr) int
+
+func osArchInit() {
+       // 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
+       //
+       // It was fixed in 5.3.15, 5.4.2, and all 5.5 and later
+       // kernels.
+       //
+       // If we're on an affected kernel, work around this issue by
+       // mlocking the top page of every signal stack. This doesn't
+       // help for signal stacks created in C, but there's not much
+       // we can do about that.
+       //
+       // TODO(austin): Remove this in Go 1.15, at which point it
+       // will be unlikely to encounter any of the affected kernels
+       // in the wild.
+
+       var uts new_utsname
+       if uname(&uts) < 0 {
+               throw("uname failed")
+       }
+       // Check for null terminator to ensure gostringnocopy doesn't
+       // walk off the end of the release string.
+       found := false
+       for _, b := range uts.release {
+               if b == 0 {
+                       found = true
+                       break
+               }
+       }
+       if !found {
+               return
+       }
+       rel := gostringnocopy(&uts.release[0])
+
+       major, minor, patch, ok := parseRelease(rel)
+       if !ok {
+               return
+       }
+
+       if major == 5 && (minor == 2 || minor == 3 && patch < 15 || minor == 4 && patch < 2) {
+               gsignalInitQuirk = mlockGsignal
+               if m0.gsignal != nil {
+                       throw("gsignal quirk too late")
+               }
+       }
+}
+
+func mlockGsignal(gsignal *g) {
+       mlock(gsignal.stack.hi-physPageSize, physPageSize)
+}
index 5f89c30f7ab54eefe1989cdc3f08516c5a5415d2..b590da750f6f48021e7ebbb568c4f48d55fe852d 100644 (file)
@@ -39,6 +39,8 @@ func archauxv(tag, val uintptr) {
        }
 }
 
+func osArchInit() {}
+
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
index b51bc8882012d23548d26baa597c2af43a9f37db..19968dc164b7c63e40b4cd8f64caf56ce0c7b867 100644 (file)
@@ -27,6 +27,8 @@ func archauxv(tag, val uintptr) {
        }
 }
 
+func osArchInit() {}
+
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
index 59d2a8f2c6efcdcf34ef0dcbe69e3963cf2ec038..464a26a8a46460e2c679dddb9909ef4b6f1dad7c 100644 (file)
@@ -10,6 +10,8 @@ package runtime
 func archauxv(tag, val uintptr) {
 }
 
+func osArchInit() {}
+
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
index ccdc3a7fe56fd38315f01cfb8c6f851ac0d4336c..87962ed982f1c49981baf889fdb9a6e08a92029b 100644 (file)
@@ -10,6 +10,8 @@ package runtime
 func archauxv(tag, val uintptr) {
 }
 
+func osArchInit() {}
+
 //go:nosplit
 func cputicks() int64 {
        // Currently cputicks() is used in blocking profiler and to seed fastrand().
index cc79cc4a66c3110638a10a96cdf7cec669881c4f..3aedc23ef90e227d856974074e653f5a3766a4e1 100644 (file)
@@ -20,3 +20,5 @@ func archauxv(tag, val uintptr) {
                cpu.HWCap2 = uint(val)
        }
 }
+
+func osArchInit() {}
index 55d35c7cff1cf34aba3b8665a554a866f2763bf5..ee18fd1dc20b90a3dcc9bd7790cae3232854049f 100644 (file)
@@ -17,3 +17,5 @@ func archauxv(tag, val uintptr) {
                cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0
        }
 }
+
+func osArchInit() {}
index d16060f6fa2f77689afe98434e82f6ca13963792..174120f887d21c2d2553b22a0bd498059a86d592 100644 (file)
 #define SYS_clone              56
 #define SYS_exit               60
 #define SYS_kill               62
+#define SYS_uname              63
 #define SYS_fcntl              72
 #define SYS_sigaltstack        131
+#define SYS_mlock              149
 #define SYS_arch_prctl         158
 #define SYS_gettid             186
 #define SYS_futex              202
@@ -764,3 +766,20 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-8
        SYSCALL
        MOVQ    AX, ret+0(FP)
        RET
+
+// func uname(utsname *new_utsname) int
+TEXT ·uname(SB),NOSPLIT,$0-16
+       MOVQ    utsname+0(FP), DI
+       MOVL    $SYS_uname, AX
+       SYSCALL
+       MOVQ    AX, ret+8(FP)
+       RET
+
+// func mlock(addr, len uintptr) int
+TEXT ·mlock(SB),NOSPLIT,$0-24
+       MOVQ    addr+0(FP), DI
+       MOVQ    len+8(FP), SI
+       MOVL    $SYS_mlock, AX
+       SYSCALL
+       MOVQ    AX, ret+16(FP)
+       RET