blob: 97f870707de7de08f4e28253f69442f007a98131 [file] [log] [blame]
// 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
import (
"runtime/internal/atomic"
"unsafe"
)
//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 == 4 && patch < 2 {
// All 5.4 versions of Ubuntu are patched.
procVersion := []byte("/proc/version\000")
f := open(&procVersion[0], _O_RDONLY, 0)
if f >= 0 {
var buf [512]byte
p := noescape(unsafe.Pointer(&buf[0]))
n := read(f, p, int32(len(buf)))
closefd(f)
needle := []byte("Ubuntu")
contains:
for i, c := range buf[:n] {
if c != needle[0] {
continue
}
if int(n)-i < len(needle) {
break
}
for j, c2 := range needle {
if c2 != buf[i+j] {
continue contains
}
}
// This is an Ubuntu system.
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")
}
throwReportQuirk = throwBadKernel
}
}
func mlockGsignal(gsignal *g) {
if atomic.Load(&touchStackBeforeSignal) != 0 {
// mlock has already failed, don't try again.
return
}
// This mlock call may fail, but we don't report the failure.
// Instead, if something goes badly wrong, we rely on prepareSignalM
// 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)
}
}