go: runtime: use of AddVectoredExceptionHandler on windows is ill-advised
What version of Go are you using (go version
)?
> go version go version go1.19 windows/arm64
Does this issue reproduce with the latest release?
Yes, with the latest major version (1.19).
What operating system and processor architecture are you using (go env
)?
go env
Output
> go env set GO111MODULE= set GOARCH=arm64 set GOBIN= set GOCACHE=C:\Users\billziss\AppData\Local\go-build set GOENV=C:\Users\billziss\AppData\Roaming\go\env set GOEXE=.exe set GOEXPERIMENT= set GOFLAGS= set GOHOSTARCH=arm64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Users\billziss\go\pkg\mod set GONOPROXY= set GONOSUMDB= set GOOS=windows set GOPATH=C:\Users\billziss\go set GOPRIVATE= set GOPROXY=https://proxy.golang.org,direct set GOROOT=C:\Program Files\Go set GOSUMDB=sum.golang.org set GOTMPDIR= set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_arm64 set GOVCS= set GOVERSION=go1.19 set GCCGO=gccgo set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=NUL set GOWORK= set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config set GOGCCFLAGS=-mthreads -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Users\billziss\AppData\Local\Temp\go-build481145130=/tmp/go-build -gno-record-gcc-switches
What did you do?
I ran a Golang program that uses a DLL that raises a benign Windows exception in a non-Golang thread. This results in a call to runtime.badsignal2
.
What did you expect to see?
I expected the program to run without any problems as the Windows API can handle the benign exception internally.
What did you see instead?
On windows/arm64 I saw a crash because of related problem #56080.
Problem and solution
The initExceptionHandler
uses AddVectoredExceptionHandler
to add a Vectored Exception Handler. This use looks ill-advised. Windows components (and some third party applications and DLLs) regularly use Structured Exception Handling to report benign errrors, such as “Access Denied”. Vectored Exception Handlers run prior to the regular Structured Exception Handlers in Windows. This means that the Golang runtime preempts the regular processing of benign errors and mistakenly thinks that an unrecoverable error has happened.
A likely fix is not to call AddVectoredExceptionHandler
.
See also
#56080 https://github.com/rclone/rclone/issues/5828#issuecomment-1269934140
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 15 (13 by maintainers)
Commits related to this issue
- runtime: ignore exceptions from non-Go threads on windows arm/arm64 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 ... — committed to golang/go by qmuntal 2 years ago
- runtime: ignore exceptions from non-Go threads on windows arm/arm64 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 ... — committed to TroutSoftware/go by qmuntal 2 years ago
@qmuntal
I have created throw-away repo golang-veh which allows for some experimentation with the following scenarios:
RaiseExcept
: Raise and catch an exception in a Go thread.RaiseNoExcept
: Raise but do not catch an exception in a Go thread.ThreadRaiseExcept
: Raise and catch an exception in a non-Go thread.ThreadRaiseNoExcept
: Raise but do not catch an exception in a non-Go thread.I then tried these scenarios with and without the debugger (WinDbg Preview) on x64 and arm64.
Results for Go threads were good (with or without the debugger):
RaiseExcept
: Raising and catching an exception in a Go thread works as expected. (Although I confirmed under WinDbg thatsigtramp
is called, because of Golang’s use of vectored exception handlers.)RaiseNoExcept
: Raising and not catching the exception in a Go thread also works as expected, in that the Golang runtime catches the exception and prints a reasonable stack trace.Results for non-Go threads were not good:
ThreadRaiseExcept
:sigtramp
is called, because of Golang’s use of vectored exception handlers.)badsignal2
(#56080)badsignal2
, but by chance theR3
register is initialized to0
, which allows the program to printruntime: signal received on thread not created by Go
endlessly. (The endless printing is because the exception handling mechanism is reentered because of howruntime.abort
tries to abort the process by causing another exception.)ThreadRaiseNoExcept
:ThreadRaiseNoExcept
. It should not since we raised an exception that is not being caught. (This is likely because the debugger is able to continue exceptions.)ThreadRaiseExcept
.Overall it looks to me that the exception handling code for Windows needs some TLC.
EDIT: My tests were done with:
AFAIK it shouldn’t, if a CL for this happens I’ll be happy to check that it works with delve.