From 1c8d1f45ba7b72836dfe93fc680dfb27ef174965 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 8 Dec 2019 22:24:10 -0500 Subject: runtime: mlock top of signal stack on both amd64 and 386 CL 209899 worked around an issue that corrupts vector registers in recent versions of the Linux kernel by mlocking the top page of every signal stack on amd64. However, the underlying issue also affects the XMM registers on 386. This CL applies the mlock fix to both amd64 and 386. Fixes #35777 (again). Change-Id: I9886f2dc4c23625421296bd5518d5fd3288bfe48 Reviewed-on: https://go-review.googlesource.com/c/go/+/210345 Run-TryBot: Austin Clements Reviewed-by: Brad Fitzpatrick Reviewed-by: Ian Lance Taylor Reviewed-by: David Chase TryBot-Result: Gobot Gobot --- src/runtime/defs_linux_386.go | 11 +++++++ src/runtime/os_linux_386.go | 7 ---- src/runtime/os_linux_amd64.go | 71 ----------------------------------------- src/runtime/os_linux_x86.go | 74 +++++++++++++++++++++++++++++++++++++++++++ src/runtime/sys_linux_386.s | 19 +++++++++++ 5 files changed, 104 insertions(+), 78 deletions(-) delete mode 100644 src/runtime/os_linux_386.go delete mode 100644 src/runtime/os_linux_amd64.go create mode 100644 src/runtime/os_linux_x86.go diff --git a/src/runtime/defs_linux_386.go b/src/runtime/defs_linux_386.go index ba349845cf..ba5ef18e02 100644 --- a/src/runtime/defs_linux_386.go +++ b/src/runtime/defs_linux_386.go @@ -227,3 +227,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 +} diff --git a/src/runtime/os_linux_386.go b/src/runtime/os_linux_386.go deleted file mode 100644 index 9be88a5ad2..0000000000 --- a/src/runtime/os_linux_386.go +++ /dev/null @@ -1,7 +0,0 @@ -// 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 deleted file mode 100644 index cbfcf2e40a..0000000000 --- a/src/runtime/os_linux_amd64.go +++ /dev/null @@ -1,71 +0,0 @@ -// 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) { - if err := mlock(gsignal.stack.hi-physPageSize, physPageSize); err < 0 { - printlock() - println("runtime: mlock of signal stack failed:", -err) - if err == -_ENOMEM { - println("runtime: increase the mlock limit (ulimit -l) or") - } - println("runtime: update your kernel to 5.4.2 or later") - throw("mlock failed") - } -} diff --git a/src/runtime/os_linux_x86.go b/src/runtime/os_linux_x86.go new file mode 100644 index 0000000000..61c51f2327 --- /dev/null +++ b/src/runtime/os_linux_x86.go @@ -0,0 +1,74 @@ +// 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. + +// +build linux +// +build 386 amd64 + +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) { + if err := mlock(gsignal.stack.hi-physPageSize, physPageSize); err < 0 { + printlock() + println("runtime: mlock of signal stack failed:", -err) + if err == -_ENOMEM { + println("runtime: increase the mlock limit (ulimit -l) or") + } + println("runtime: update your kernel to 5.4.2 or later") + throw("mlock failed") + } +} diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 373d9d3bc2..8e05acf894 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -39,6 +39,8 @@ #define SYS_socketcall 102 #define SYS_setittimer 104 #define SYS_clone 120 +#define SYS_uname 122 +#define SYS_mlock 150 #define SYS_sched_yield 158 #define SYS_nanosleep 162 #define SYS_rt_sigreturn 173 @@ -776,3 +778,20 @@ TEXT runtime·sbrk0(SB),NOSPLIT,$0-4 INVOKE_SYSCALL MOVL AX, ret+0(FP) RET + +// func uname(utsname *new_utsname) int +TEXT ·uname(SB),NOSPLIT,$0-8 + MOVL $SYS_uname, AX + MOVL utsname+0(FP), BX + INVOKE_SYSCALL + MOVL AX, ret+4(FP) + RET + +// func mlock(addr, len uintptr) int +TEXT ·mlock(SB),NOSPLIT,$0-12 + MOVL $SYS_mlock, AX + MOVL addr+0(FP), BX + MOVL len+4(FP), CX + INVOKE_SYSCALL + MOVL AX, ret+8(FP) + RET -- cgit v1.3