Skip to content

runtime: fix crash during VDSO calls on arm #34030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/runtime/crash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error)
return exe, nil
}

func TestVDSO(t *testing.T) {
t.Parallel()
output := runTestProg(t, "testprog", "SignalInVDSO")
want := "success\n"
if output != want {
t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want);
}
}

var (
staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
staleRuntimeErr error
Expand Down
27 changes: 21 additions & 6 deletions src/runtime/signal_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,21 @@ func sigpipe() {
dieFromSignal(_SIGPIPE)
}

// sigFetchG fetches the value of G safely when running in a signal handler.
// On some architectures, the g value may be clobbered when running in a VDSO.
// See issue #32912.
//
//go:nosplit
func sigFetchG(c *sigctxt) *g {
switch GOARCH {
case "arm", "arm64", "ppc64", "ppc64le":
if inVDSOPage(c.sigpc()) {
return nil
}
}
return getg()
}

// sigtrampgo is called from the signal handler function, sigtramp,
// written in assembly code.
// This is called by the signal handler, and the world may be stopped.
Expand All @@ -289,9 +304,9 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
if sigfwdgo(sig, info, ctx) {
return
}
g := getg()
c := &sigctxt{info, ctx}
g := sigFetchG(c)
if g == nil {
c := &sigctxt{info, ctx}
if sig == _SIGPROF {
sigprofNonGoPC(c.sigpc())
return
Expand Down Expand Up @@ -347,7 +362,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
signalDuringFork(sig)
}

c := &sigctxt{info, ctx}
c.fixsigcode(sig)
sighandler(sig, info, ctx, g)
setg(g)
Expand Down Expand Up @@ -657,9 +671,10 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
return false
}
// Determine if the signal occurred inside Go code. We test that:
// (1) we were in a goroutine (i.e., m.curg != nil), and
// (2) we weren't in CGO.
g := getg()
// (1) we weren't in VDSO page,
// (2) we were in a goroutine (i.e., m.curg != nil), and
// (3) we weren't in CGO.
g := sigFetchG(c)
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
return false
}
Expand Down
55 changes: 55 additions & 0 deletions src/runtime/testdata/testprog/vdso.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.

// Invoke signal hander in the VDSO context (see issue 32912).

package main

import (
"fmt"
"io/ioutil"
"os"
"runtime/pprof"
"time"
)

func init() {
register("SignalInVDSO", signalInVDSO)
}

func signalInVDSO() {
f, err := ioutil.TempFile("", "timeprofnow")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}

if err := pprof.StartCPUProfile(f); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}

t0 := time.Now()
t1 := t0
// We should get a profiling signal 100 times a second,
// so running for 1 second should be sufficient.
for t1.Sub(t0) < time.Second {
t1 = time.Now()
}

pprof.StopCPUProfile()

name := f.Name()
if err := f.Close(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}

if err := os.Remove(name); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}

fmt.Println("success");
}
1 change: 1 addition & 0 deletions src/runtime/vdso_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ func vdsoauxv(tag, val uintptr) {
}

// vdsoMarker reports whether PC is on the VDSO page.
//go:nosplit
func inVDSOPage(pc uintptr) bool {
for _, k := range vdsoSymbolKeys {
if *k.ptr != 0 {
Expand Down