Repository navigation
Description
PureGo Version
v0.10.2 (via github.com/shirou/gopsutil/v4 v4.26.7 )
Operating System
- Windows
- macOS
- Linux
- FreeBSD
- NetBSD
- Android
- iOS
Go Version (go version)
1.27.2
What steps will reproduce the problem?
run go.chromium.org/build/siso with profiler enabled.
What is the expected result?
not crash
What happens instead?
signal 27 received on thread with no signal stack
golang/go#77588
Anything else you feel useful to add?
Problem Description
In internal/fakecgo/libcgo.go (with build tag //go:build !cgo && (darwin || freebsd || linux || netbsd)), the constants for pthread_sigmask's how argument are hardcoded to Linux values:
// for pthread_sigmask:
type sighow int32
const (
SIG_BLOCK sighow = 0
SIG_UNBLOCK sighow = 1
SIG_SETMASK sighow = 2
)However, on Darwin, FreeBSD, and NetBSD (and other BSD-derived systems), SIG_BLOCK, SIG_UNBLOCK, and SIG_SETMASK are 1-based rather than 0-based:
SIG_BLOCK = 1SIG_UNBLOCK = 2SIG_SETMASK = 3
(For reference, see<sys/signal.h>on Darwin/FreeBSD/NetBSD, or the Go runtime definitions insrc/runtime/os_darwin.go,src/runtime/os_freebsd.go, andsrc/runtime/os_netbsd.go).
Consequences
In _cgo_sys_thread_start (internal/fakecgo/go_darwin.go:20-31, and identically in go_freebsd.go and go_netbsd.go):
sigfillset(&ign)
pthread_sigmask(SIG_SETMASK, &ign, &oset)
...
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
pthread_sigmask(SIG_SETMASK, &oset, nil)Because SIG_SETMASK evaluates to 2, calling pthread_sigmask(2, ...) on Darwin and BSDs executes SIG_UNBLOCK (how = 2) instead of SIG_SETMASK:
- Instead of temporarily blocking all signals in
ignacrosspthread_create, all signals are unblocked on the calling thread, and the newly spawned thread inherits an all-unblocked signal mask. - After
pthread_create,pthread_sigmask(SIG_SETMASK /* 2 = SIG_UNBLOCK */, &oset, nil)unblocksosetrather than restoring the previous signal mask.
Impact / Crash Reproduction
When a Go program enables CPU profiling (runtime/pprof.StartCPUProfile) or receives signals while running with purego/internal/fakecgo active (e.g. built with CGO_ENABLED=0 on Darwin):
- A newly spawned OS thread begins executing
threadentrywithsigaltstackdisabled (SS_DISABLE, the OS default for a new pthread before Go sets up its signal stack). threadentrycallssetg_trampoline(...), installing the thread'sg0pointer into TLS. At this moment,ts.g.stackhiis set to the stack byte size (e.g.,0x80000) andts.g.stacklois0, becauseruntime.mstart0has not yet had a chance to setg0.stackto the actual OS stack bounds.- If
SIGPROF(signal 27) arrives on the new thread in the window betweensetg_trampolineandmstart0, it is delivered on the thread's normal stack (since signals were never masked, andsigaltstackis disabled). - Darwin's signal trampoline
runtime.sigtrampruns, loads the non-nilg0from TLS, and callsruntime.sigtrampgo. runtime.sigtrampgoobserves thatsigaltstackisSS_DISABLEand checks if the current stack pointerspis withing0.stack([0, stackhi]). Sincespis in the thread's real virtual memory space (e.g.0x16...or0x7f...), this check fails.- The Go runtime assumes an external non-Go thread disabled
sigaltstackand immediately crashes:(This is the root cause behind runtime: signal 27 received on thread with no signal stack golang/go#77588).signal 27 received on thread with no signal stack fatal error: non-Go code disabled sigaltstack
Suggested Fix
Move the definition of SIG_BLOCK, SIG_UNBLOCK, and SIG_SETMASK into OS-specific files (e.g., in libcgo_linux.go and libcgo_darwin.go/libcgo_freebsd.go/libcgo_netbsd.go, or dedicated build-tagged files):
Linux:
const (
SIG_BLOCK sighow = 0
SIG_UNBLOCK sighow = 1
SIG_SETMASK sighow = 2
)Darwin, FreeBSD, NetBSD:
const (
SIG_BLOCK sighow = 1
SIG_UNBLOCK sighow = 2
SIG_SETMASK sighow = 3
)
Activity
TotallyGamerJet commentedon Oct 15, 2026
Seems like this should be cherry picked
internal/fakecgo: use per-OS values for pthread_sigmask how (#553)
hajimehoshi commentedon Oct 15, 2026
I've cherry-picked the fix to 0.11.
@ukai Please use
0fd9f62f2e6b53025cd8bb5c29d98350de5c6e77until I add a tagv0.11.2a few days later. CC @shirou for gopsutilfix(fakecgo): use per-OS values for pthread_sigmask how
fix(fakecgo): use per-OS values for pthread_sigmask how
fix(fakecgo): use per-OS values for pthread_sigmask how
internal/fakecgo: use per-OS values for pthread_sigmask how (ebitengi…