Skip to content

Commit

Permalink
runtime: ignore exceptions from non-Go threads on windows arm/arm64
Browse files Browse the repository at this point in the history
If there is no current G while handling an exception it means
the exception was originated in a non-Go thread.

The best we can do is ignore the exception and let it flow
through other vectored and structured error handlers.

I've removed badsignal2 from sigtramp because we can't really know
if the signal is bad or not, it might be handled later in the chain.

Fixes #50877
Updates #56082

Change-Id: Ica159eb843629986d1fb5482f0b59a9c1ed91698
Reviewed-on: https://go-review.googlesource.com/c/go/+/442896
Reviewed-by: Alex Brainman <[email protected]>
Auto-Submit: Michael Pratt <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Michael Pratt <[email protected]>
Run-TryBot: Quim Muntal <[email protected]>
Reviewed-by: David Chase <[email protected]>
  • Loading branch information
qmuntal authored and gopherbot committed Oct 19, 2022
1 parent 4a164a4 commit 6fe3ccd
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 5 deletions.
55 changes: 55 additions & 0 deletions src/runtime/signal_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,61 @@ import (
"testing"
)

func TestVectoredHandlerExceptionInNonGoThread(t *testing.T) {
if *flagQuick {
t.Skip("-quick")
}
testenv.MustHaveGoBuild(t)
testenv.MustHaveCGO(t)
testenv.MustHaveExecPath(t, "g++")
testprog.Lock()
defer testprog.Unlock()
dir := t.TempDir()

// build c program
dll := filepath.Join(dir, "veh.dll")
cmd := exec.Command("g++", "-shared", "-o", dll, "testdata/testwinlibthrow/veh.cpp", "-static", "-lstdc++")
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil {
t.Fatalf("failed to build c exe: %s\n%s", err, out)
}

// build go exe
exe := filepath.Join(dir, "test.exe")
cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinlibthrow/main.go")
out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil {
t.Fatalf("failed to build go library: %s\n%s", err, out)
}

// run test program in same thread
cmd = exec.Command(exe)
out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
if err == nil {
t.Fatal("error expected")
}
if _, ok := err.(*exec.ExitError); ok && len(out) > 0 {
if !bytes.Contains(out, []byte("Exception 0x2a")) {
t.Fatalf("unexpected failure while running executable: %s\n%s", err, out)
}
} else {
t.Fatalf("unexpected error while running executable: %s\n%s", err, out)
}
// run test program in a new thread
cmd = exec.Command(exe, "thread")
out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
if err == nil {
t.Fatal("error expected")
}
if err, ok := err.(*exec.ExitError); ok {
if err.ExitCode() != 42 {
t.Fatalf("unexpected failure while running executable: %s\n%s", err, out)
}
} else {
t.Fatalf("unexpected error while running executable: %s\n%s", err, out)
}
}

func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) {
if *flagQuick {
t.Skip("-quick")
Expand Down
10 changes: 8 additions & 2 deletions src/runtime/sys_windows_arm.s
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,14 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0
MOVW R1, R7 // Save param1

BL runtime·load_g(SB)
CMP $0, g // is there a current g?
BL.EQ runtime·badsignal2(SB)
CMP $0, g // is there a current g?
BNE g_ok
ADD $(8+20), R13 // free locals
MOVM.IA.W (R13), [R3, R4-R11, R14] // pop {r3, r4-r11, lr}
MOVW $0, R0 // continue
BEQ return

g_ok:

// save g and SP in case of stack switch
MOVW R13, 24(R13)
Expand Down
11 changes: 8 additions & 3 deletions src/runtime/sys_windows_arm64.s
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,15 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0
MOVD g, R17 // saved R28 (callee-save from Windows, not really g)

BL runtime·load_g(SB) // smashes R0, R27, R28 (g)
CMP $0, g // is there a current g?
BNE 2(PC)
BL runtime·badsignal2(SB)
CMP $0, g // is there a current g?
BNE g_ok
MOVD R7, LR
MOVD R16, R27 // restore R27
MOVD R17, g // restore R28
MOVD $0, R0 // continue
RET

g_ok:
// Do we need to switch to the g0 stack?
MOVD g, R3 // R3 = oldg (for sigtramp_g0)
MOVD g_m(g), R2 // R2 = m
Expand Down
23 changes: 23 additions & 0 deletions src/runtime/testdata/testwinlibthrow/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"os"
"syscall"
)

func main() {
dll := syscall.MustLoadDLL("veh.dll")
RaiseExcept := dll.MustFindProc("RaiseExcept")
RaiseNoExcept := dll.MustFindProc("RaiseNoExcept")
ThreadRaiseExcept := dll.MustFindProc("ThreadRaiseExcept")
ThreadRaiseNoExcept := dll.MustFindProc("ThreadRaiseNoExcept")

thread := len(os.Args) > 1 && os.Args[1] == "thread"
if !thread {
RaiseExcept.Call()
RaiseNoExcept.Call()
} else {
ThreadRaiseExcept.Call()
ThreadRaiseNoExcept.Call()
}
}
53 changes: 53 additions & 0 deletions src/runtime/testdata/testwinlibthrow/veh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//go:build ignore

#include <windows.h>

extern "C" __declspec(dllexport)
void RaiseExcept(void)
{
try
{
RaiseException(42, 0, 0, 0);
}
catch (...)
{
}
}

extern "C" __declspec(dllexport)
void RaiseNoExcept(void)
{
RaiseException(42, 0, 0, 0);
}

static DWORD WINAPI ThreadRaiser(void* Context)
{
if (Context)
RaiseExcept();
else
RaiseNoExcept();
return 0;
}

static void ThreadRaiseXxx(int except)
{
static int dummy;
HANDLE thread = CreateThread(0, 0, ThreadRaiser, except ? &dummy : 0, 0, 0);
if (0 != thread)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
}

extern "C" __declspec(dllexport)
void ThreadRaiseExcept(void)
{
ThreadRaiseXxx(1);
}

extern "C" __declspec(dllexport)
void ThreadRaiseNoExcept(void)
{
ThreadRaiseXxx(0);
}

0 comments on commit 6fe3ccd

Please sign in to comment.