Description
What version of Go are you using (go version
)?
go version go1.10.4 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOEXE="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOOS="linux" GOPATH="/home/jim/go" GORACE="" GOROOT="/usr/lib/go-1.10" GOTMPDIR="" GOTOOLDIR="/usr/lib/go-1.10/pkg/tool/linux_amd64" GCCGO="/usr/bin/gccgo" CC="gcc" CXX="g++" CGO_ENABLED="1" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build690985833=/tmp/go-build -gno-record-gcc-switches"
What did you do?
-
- Compile a library using -buildmode=c-shared
-
- Trap SIGSEGV in a C program with a handler which will reraise SIGSEGV
-
- Load the library against the C program using dlopen
-
- Cause the C program to SEGSIGV by dereference a NULL pointer
src file: infinitesignalloop.gz
to compile: uncompress src file to GOPATH and run make
to reproduce: just run ./trap_signal before
What did you expect to see?
The program should crash finally with the default system segfault handler be called.
trap SIGSEGV before dlopen ./libdummycgo.so
Received Segmentation fault ...
Segmentation fault (core dumped)
What did you see instead?
A infinite sighandling loop has happen.
trap SIGSEGV before dlopen ./libdummycgo.so
Received Segmentation fault ...
Received Segmentation fault ...
... // infinite loop
Received Segmentation fault ...
Killed
Following the code with gdb, it seems that Go signal handler raises SIGSEGV again to invoke the C handler, then C handler reraise SIGSEGV, which is catched by Go signal handler again, and then repeat and repeat.
As doc https://golang.org/pkg/os/signal/#hdr-Go_programs_that_use_cgo_or_SWIG said in section Go programs that use cgo or SWIG (not in section Non-Go programs that call Go code)
If the Go signal handler is invoked on a non-Go thread not running Go code, the handler generally forwards the signal to the non-Go code, as follows. If the signal is SIGPROF, the Go handler does nothing. Otherwise, the Go handler removes itself, unblocks the signal, and raises it again, to invoke any non-Go handler or default system handler. If the program does not exit, the Go handler then reinstalls itself and continues execution of the program.
And if we trap SIGSEGV after load the library (just run ./trap_signal after
), there C program will just crash with system default handler called as expected.
Some more tests shows above issue exist for all synchronize signals (SIGBUS, SIGFPE, and SIGSEGV).
When not use dlopen to load the library, instead to link the library against to a C program as following,
-
- Compile a library using -buildmode=c-shared
-
- Trap SIGSEGV in a C program with a handler which will reraise SIGSEGV
-
- Link the library against the C program
-
- Cause the C program to SEGSIGV by dereference a NULL pointer
and then run the C program, it will crash with default segfault handler called.
- Cause the C program to SEGSIGV by dereference a NULL pointer