mirror of
https://github.com/golang/go.git
synced 2025-05-16 21:04:38 +00:00
runtime: don't crash on mlock failure
Instead, note that mlock has failed, start trying the mitigation of touching the signal stack before sending a preemption signal, and, if the program crashes, mention the possible problem and a wiki page describing the issue (https://golang.org/wiki/LinuxKernelSignalVectorBug). Tested on a kernel in the buggy version range, but with the patch, by using `ulimit -l 0`. Fixes #37436 Change-Id: I072aadb2101496dffd655e442fa5c367dad46ce8 Reviewed-on: https://go-review.googlesource.com/c/go/+/223121 Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
dcc5c24926
commit
b851e51160
@ -5,6 +5,7 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime/internal/atomic"
|
||||||
"runtime/internal/sys"
|
"runtime/internal/sys"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -479,7 +480,21 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
|
|||||||
func getpid() int
|
func getpid() int
|
||||||
func tgkill(tgid, tid, sig int)
|
func tgkill(tgid, tid, sig int)
|
||||||
|
|
||||||
|
// touchStackBeforeSignal stores an errno value. If non-zero, it means
|
||||||
|
// that we should touch the signal stack before sending a signal.
|
||||||
|
// This is used on systems that have a bug when the signal stack must
|
||||||
|
// be faulted in. See #35777 and #37436.
|
||||||
|
//
|
||||||
|
// This is accessed atomically as it is set and read in different threads.
|
||||||
|
//
|
||||||
|
// TODO(austin): Remove this after Go 1.15 when we remove the
|
||||||
|
// mlockGsignal workaround.
|
||||||
|
var touchStackBeforeSignal uint32
|
||||||
|
|
||||||
// signalM sends a signal to mp.
|
// signalM sends a signal to mp.
|
||||||
func signalM(mp *m, sig int) {
|
func signalM(mp *m, sig int) {
|
||||||
|
if atomic.Load(&touchStackBeforeSignal) != 0 {
|
||||||
|
atomic.Cas((*uint32)(unsafe.Pointer(mp.gsignal.stack.hi-4)), 0, 0)
|
||||||
|
}
|
||||||
tgkill(getpid(), int(mp.procid), sig)
|
tgkill(getpid(), int(mp.procid), sig)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
import "runtime/internal/atomic"
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func uname(utsname *new_utsname) int
|
func uname(utsname *new_utsname) int
|
||||||
|
|
||||||
@ -58,17 +60,34 @@ func osArchInit() {
|
|||||||
if m0.gsignal != nil {
|
if m0.gsignal != nil {
|
||||||
throw("gsignal quirk too late")
|
throw("gsignal quirk too late")
|
||||||
}
|
}
|
||||||
|
throwReportQuirk = throwBadKernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mlockGsignal(gsignal *g) {
|
func mlockGsignal(gsignal *g) {
|
||||||
if err := mlock(gsignal.stack.hi-physPageSize, physPageSize); err < 0 {
|
if atomic.Load(&touchStackBeforeSignal) != 0 {
|
||||||
printlock()
|
// mlock has already failed, don't try again.
|
||||||
println("runtime: mlock of signal stack failed:", -err)
|
return
|
||||||
if err == -_ENOMEM {
|
}
|
||||||
println("runtime: increase the mlock limit (ulimit -l) or")
|
|
||||||
}
|
// This mlock call may fail, but we don't report the failure.
|
||||||
println("runtime: update your kernel to 5.3.15+, 5.4.2+, or 5.5+")
|
// Instead, if something goes badly wrong, we rely on prepareSignalM
|
||||||
throw("mlock failed")
|
// and throwBadKernel to do further mitigation and to report a problem
|
||||||
|
// to the user if mitigation fails. This is because many
|
||||||
|
// systems have a limit on the total mlock size, and many kernels
|
||||||
|
// that appear to have bad versions are actually patched to avoid the
|
||||||
|
// bug described above. We want Go 1.14 to run on those systems.
|
||||||
|
// See #37436.
|
||||||
|
if errno := mlock(gsignal.stack.hi-physPageSize, physPageSize); errno < 0 {
|
||||||
|
atomic.Store(&touchStackBeforeSignal, uint32(-errno))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// throwBadKernel is called, via throwReportQuirk, by throw.
|
||||||
|
func throwBadKernel() {
|
||||||
|
if errno := atomic.Load(&touchStackBeforeSignal); errno != 0 {
|
||||||
|
println("runtime: note: your Linux kernel may be buggy")
|
||||||
|
println("runtime: note: see https://golang.org/wiki/LinuxKernelSignalVectorBug")
|
||||||
|
println("runtime: note: mlock workaround for kernel bug failed with errno", errno)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1281,6 +1281,12 @@ func startpanic_m() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// throwReportQuirk, if non-nil, is called by throw after dumping the stacks.
|
||||||
|
//
|
||||||
|
// TODO(austin): Remove this after Go 1.15 when we remove the
|
||||||
|
// mlockGsignal workaround.
|
||||||
|
var throwReportQuirk func()
|
||||||
|
|
||||||
var didothers bool
|
var didothers bool
|
||||||
var deadlock mutex
|
var deadlock mutex
|
||||||
|
|
||||||
@ -1327,6 +1333,10 @@ func dopanic_m(gp *g, pc, sp uintptr) bool {
|
|||||||
|
|
||||||
printDebugLog()
|
printDebugLog()
|
||||||
|
|
||||||
|
if throwReportQuirk != nil {
|
||||||
|
throwReportQuirk()
|
||||||
|
}
|
||||||
|
|
||||||
return docrash
|
return docrash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user