]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix unrecovered panic on external thread
authorRuss Cox <rsc@golang.org>
Wed, 29 Oct 2014 01:53:09 +0000 (21:53 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 29 Oct 2014 01:53:09 +0000 (21:53 -0400)
Fixes #8588.

LGTM=austin
R=austin
CC=golang-codereviews, khr
https://golang.org/cl/159700044

src/runtime/asm_386.s
src/runtime/asm_amd64.s
src/runtime/asm_arm.s
src/runtime/crash_cgo_test.go
src/runtime/crash_test.go

index 20d3c47c94d2f8483552d38b50b5a9d775cf485c..0d46a9eff78035a6fca583a0f4b6800c32cabcde 100644 (file)
@@ -732,6 +732,20 @@ needm:
        MOVL    g(CX), BP
        MOVL    g_m(BP), BP
 
+       // Set m->sched.sp = SP, so that if a panic happens
+       // during the function we are about to execute, it will
+       // have a valid SP to run on the g0 stack.
+       // The next few lines (after the havem label)
+       // will save this SP onto the stack and then write
+       // the same SP back to m->sched.sp. That seems redundant,
+       // but if an unrecovered panic happens, unwindm will
+       // restore the g->sched.sp from the stack location
+       // and then onM will try to use it. If we don't set it here,
+       // that restored SP will be uninitialized (typically 0) and
+       // will not be usable.
+       MOVL    m_g0(BP), SI
+       MOVL    SP, (g_sched+gobuf_sp)(SI)
+
 havem:
        // Now there's a valid m, and we're running on its m->g0.
        // Save current m->g0->sched.sp on stack and then set it to SP.
index 709834180e55a31b774d81da9b785be628790d23..a9b082beb82a66f28d9e808166f66e334415de3e 100644 (file)
@@ -717,6 +717,20 @@ needm:
        get_tls(CX)
        MOVQ    g(CX), BP
        MOVQ    g_m(BP), BP
+       
+       // Set m->sched.sp = SP, so that if a panic happens
+       // during the function we are about to execute, it will
+       // have a valid SP to run on the g0 stack.
+       // The next few lines (after the havem label)
+       // will save this SP onto the stack and then write
+       // the same SP back to m->sched.sp. That seems redundant,
+       // but if an unrecovered panic happens, unwindm will
+       // restore the g->sched.sp from the stack location
+       // and then onM will try to use it. If we don't set it here,
+       // that restored SP will be uninitialized (typically 0) and
+       // will not be usable.
+       MOVQ    m_g0(BP), SI
+       MOVQ    SP, (g_sched+gobuf_sp)(SI)
 
 havem:
        // Now there's a valid m, and we're running on its m->g0.
index 621d13187acf0df513c652b4a73a16fc994d86f6..e94b4c1ff61560f3424b45ff0dc9c758980e1dae 100644 (file)
@@ -556,6 +556,21 @@ TEXT       ·cgocallback_gofunc(SB),NOSPLIT,$8-12
        MOVW    $runtime·needm(SB), R0
        BL      (R0)
 
+       // Set m->sched.sp = SP, so that if a panic happens
+       // during the function we are about to execute, it will
+       // have a valid SP to run on the g0 stack.
+       // The next few lines (after the havem label)
+       // will save this SP onto the stack and then write
+       // the same SP back to m->sched.sp. That seems redundant,
+       // but if an unrecovered panic happens, unwindm will
+       // restore the g->sched.sp from the stack location
+       // and then onM will try to use it. If we don't set it here,
+       // that restored SP will be uninitialized (typically 0) and
+       // will not be usable.
+       MOVW    g_m(g), R8
+       MOVW    m_g0(R8), R3
+       MOVW    R13, (g_sched+gobuf_sp)(R3)
+
 havem:
        MOVW    g_m(g), R8
        MOVW    R8, savedm-4(SP)
index 4ff0084c2286776900ac543f792a0ad5c93ad363..7877965587738af2b19e8aec53aab5d7735f0f10 100644 (file)
@@ -8,6 +8,7 @@ package runtime_test
 
 import (
        "runtime"
+       "strings"
        "testing"
 )
 
@@ -34,6 +35,14 @@ func TestCgoTraceback(t *testing.T) {
        }
 }
 
+func TestCgoExternalThreadPanic(t *testing.T) {
+       got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC)
+       want := "panic: BOOM"
+       if !strings.Contains(got, want) {
+               t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
+       }
+}
+
 const cgoSignalDeadlockSource = `
 package main
 
@@ -117,3 +126,43 @@ func main() {
        fmt.Printf("OK\n")
 }
 `
+
+const cgoExternalThreadPanicSource = `
+package main
+
+// void start(void);
+import "C"
+
+func main() {
+       C.start()
+       select {}
+}
+
+//export gopanic
+func gopanic() {
+       panic("BOOM")
+}
+`
+
+const cgoExternalThreadPanicC = `
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+
+void gopanic(void);
+
+static void*
+die(void* x)
+{
+       gopanic();
+       return 0;
+}
+
+void
+start(void)
+{
+       pthread_t t;
+       if(pthread_create(&t, 0, die, 0) != 0)
+               printf("pthread_create failed\n");
+}
+`
index 783b4c48f521f9aa593f4bff03f0269ad9232417..211a0476fd51eef065946c6837cd2a5959f71e8a 100644 (file)
@@ -31,7 +31,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
        return cmd
 }
 
-func executeTest(t *testing.T, templ string, data interface{}) string {
+func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
        switch runtime.GOOS {
        case "android", "nacl":
                t.Skipf("skipping on %s", runtime.GOOS)
@@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
                t.Fatalf("failed to close file: %v", err)
        }
 
-       got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput()
+       for i := 0; i < len(extra); i += 2 {
+               if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       cmd := exec.Command("go", "build", "-o", "a.exe")
+       cmd.Dir = dir
+       out, err := testEnv(cmd).CombinedOutput()
+       if err != nil {
+               t.Fatalf("building source: %v\n%s", err, out)
+       }
+
+       got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
        return string(got)
 }