Skip to content

Commit a5f71ea

Browse files
committed
runtime: do not crash in lastcontinuehandler when running as DLL
If Go DLL is used by external C program, and lastcontinuehandler is reached, lastcontinuehandler will crash the process it is running in. But it should not be up to Go runtime to decide if process to be crashed or not - it should be up to C runtime. This CL adjusts lastcontinuehandler to not to crash when running as DLL. Fixes #32648.
1 parent 1c7a0ff commit a5f71ea

File tree

5 files changed

+40
-104
lines changed

5 files changed

+40
-104
lines changed

src/runtime/signal_windows.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ var testingWER bool
171171
//
172172
//go:nosplit
173173
func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
174+
if islibrary || isarchive {
175+
// Go DLL/archive has been loaded in a non-go program.
176+
// If the exception does not originate from go, the go runtime
177+
// should not take responsibility of crashing the process
178+
return _EXCEPTION_CONTINUE_SEARCH
179+
}
174180
if testingWER {
175181
return _EXCEPTION_CONTINUE_SEARCH
176182
}

src/runtime/signal_windows_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package runtime_test
55
import (
66
"internal/testenv"
77
"io/ioutil"
8+
"os"
89
"os/exec"
910
"path/filepath"
1011
"runtime"
@@ -27,6 +28,7 @@ func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) {
2728
if err != nil {
2829
t.Fatalf("failed to create temp directory: %v", err)
2930
}
31+
defer os.Remove(dir)
3032

3133
// build go dll
3234
dll := filepath.Join(dir, "testwinlib.dll")
@@ -38,7 +40,7 @@ func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) {
3840

3941
// build c program
4042
exe := filepath.Join(dir, "test.exe")
41-
cmd = exec.Command("gcc", "-L"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c")
43+
cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c")
4244
out, err = testenv.CleanCmdEnv(cmd).CombinedOutput()
4345
if err != nil {
4446
t.Fatalf("failed to build c exe: %s\n%s", err, out)
Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,57 @@
11
#include <stdio.h>
22
#include <windows.h>
3-
#include "main.h"
3+
#include "testwinlib.h"
4+
45
int exceptionCount;
56
int continueCount;
6-
LONG WINAPI customExceptionHandlder(struct _EXCEPTION_POINTERS *ExceptionInfo){
7-
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT){
8-
exceptionCount++;
7+
LONG WINAPI customExceptionHandlder(struct _EXCEPTION_POINTERS *ExceptionInfo)
8+
{
9+
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
10+
{
11+
exceptionCount++;
912
// prepare context to resume execution
1013
CONTEXT *c = ExceptionInfo->ContextRecord;
11-
c->Rip = *(ULONG_PTR *) c->Rsp;
12-
c->Rsp += 8;
14+
c->Rip = *(ULONG_PTR *)c->Rsp;
15+
c->Rsp += 8;
1316
return EXCEPTION_CONTINUE_EXECUTION;
1417
}
1518
return EXCEPTION_CONTINUE_SEARCH;
1619
}
17-
LONG WINAPI customContinueHandlder(struct _EXCEPTION_POINTERS *ExceptionInfo){
18-
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT){
19-
continueCount++;
20+
LONG WINAPI customContinueHandlder(struct _EXCEPTION_POINTERS *ExceptionInfo)
21+
{
22+
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
23+
{
24+
continueCount++;
2025
return EXCEPTION_CONTINUE_EXECUTION;
2126
}
2227
return EXCEPTION_CONTINUE_SEARCH;
2328
}
2429

25-
void throwFromC(){
30+
void throwFromC()
31+
{
2632
DebugBreak();
2733
}
28-
int main(){
34+
int main()
35+
{
2936
// simulate a "lazily" attached debugger, by calling some go code before attaching the exception/continue handler
3037
Dummy();
3138
exceptionCount = 0;
32-
continueCount =0;
39+
continueCount = 0;
3340
void *exceptionHandlerHandle = AddVectoredExceptionHandler(0, customExceptionHandlder);
34-
if(NULL == exceptionHandlerHandle){
41+
if (NULL == exceptionHandlerHandle)
42+
{
3543
printf("cannot add vectored exception handler\n");
3644
return 2;
3745
}
3846
void *continueHandlerHandle = AddVectoredContinueHandler(0, customContinueHandlder);
39-
if(NULL == continueHandlerHandle){
47+
if (NULL == continueHandlerHandle)
48+
{
4049
printf("cannot add vectored continue handler\n");
4150
return 2;
4251
}
4352
CallMeBack(throwFromC);
44-
//RemoveVectoredContinueHandler(exceptionHandlerHandle);
53+
RemoveVectoredContinueHandler(continueHandlerHandle);
54+
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
4555
printf("exceptionCount: %d\ncontinueCount: %d\n", exceptionCount, continueCount);
4656
return 0;
4757
}

src/runtime/testdata/testwinlib/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@ package main
99
//}
1010
import "C"
1111

12-
// CallMeBack call backs C code
12+
// CallMeBack call backs C code.
1313
//export CallMeBack
1414
func CallMeBack(callback C.callmeBackFunc) {
1515
C.bridgeCallback(callback)
1616
}
1717

18-
// Dummy is there to bootstrap the go runtime before setting up the "debugger" continue handlers
18+
// Dummy is called by the C code before registering the exception/continue handlers simulating a debugger.
19+
// This makes sure that the Go runtime's lastcontinuehandler is reached before the C continue handler and thus,
20+
// validate that it does not crash the program before another handler could take an action.
21+
// The idea here is to reproduce what happens when you attach a debugger to a running program.
22+
// It also simulate the behavior of the .Net debugger, which register its exception/continue handlers lazily.
1923
//export Dummy
2024
func Dummy() int {
2125
return 42

src/runtime/testdata/testwinlib/main.h

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)