| // 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) |
| } |
| } |