Repository navigation
fix(fakecgo): use per-OS values for pthread_sigmask how (macOS/FreeBSD signal mask) - #90
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
kolkov
force-pushed
the
fix/fakecgo-sigmask-constants
branch
from
October 15, 2026 19:01
af0f71f to
090505d
Compare
SIG_BLOCK, SIG_UNBLOCK and SIG_SETMASK were defined once in libcgo.go
with the Linux values 0/1/2. Darwin, FreeBSD and NetBSD use 1/2/3, so
pthread_sigmask(SIG_SETMASK, ...) in _cgo_sys_thread_start ran as
SIG_UNBLOCK there: new runtime threads started with every signal
unblocked instead of blocked, and the restore call unblocked the saved
mask instead of reinstating it. A signal arriving before the new thread
had its g0 stack set up could crash the process.
Define the constants in libcgo_{darwin,freebsd,linux,netbsd}.go with
each system's values. Linux and Android were not affected.
Add TestSighowMatchesSystemHeaders, which compiles a probe with the
system C compiler and checks the constants against the host's
<signal.h>, so CI on macOS catches a mismatch.
Ported from ebitengine/purego#553 (ebitengine/purego#552).
The test job ran only ./ffi and ./types (with coverage), so no test under internal/ ever ran in CI, including the new TestSighowMatchesSystemHeaders in internal/fakecgo, which is only meaningful against each OS's real <signal.h>. Add a separate step that runs go test ./internal/... without coverage, so the coverage numbers are unchanged. Packages excluded on a target by build constraints are skipped by the pattern.
kolkov
force-pushed
the
fix/fakecgo-sigmask-constants
branch
from
October 15, 2026 19:10
090505d to
26a9bdb
Compare
Contributor
Author
|
@lkmavi this is now merged to git checkout main && git pull
CGO_ENABLED=0 go test -count=1 -v -run TestSighowMatchesSystemHeaders ./internal/fakecgo
CGO_ENABLED=0 go test -count=1 ./...
|
Contributor
|
@kolkov I started it, the tests passed. |
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
On macOS and FreeBSD with
CGO_ENABLED=0, every new Go runtime thread started with all signals unblocked instead of blocked.internal/fakecgodefined thepthread_sigmaskhowconstants once, with the Linux values:Darwin, FreeBSD and NetBSD use 1/2/3 (see the header links in the diff). So in
_cgo_sys_thread_start(go_darwin_*.go,go_freebsd_*.go):pthread_sigmask(SIG_SETMASK, &all, &old)ran asSIG_UNBLOCK, which unblocked every signal acrosspthread_createinstead of blocking them, and the new thread inherited that mask;pthread_sigmask(SIG_SETMASK, &old, nil)ran asSIG_UNBLOCKtoo, which unblocked the signals in the saved mask instead of reinstating it.A signal that lands on the new thread before the runtime has set up its g0 stack can crash the process. purego hit exactly this on macOS under the profiler:
signal 27 received on thread with no signal stack(purego#552, golang/go#77588). goffi'sinternal/fakecgoderives from purego's and carries the same code. Linux is unaffected (the values match), and so is Android (its ownlibcgo_android.goalready has the correct values).Fix
Ported from ebitengine/purego#553: the constants move out of the shared
libcgo.gointolibcgo_{darwin,freebsd,linux,netbsd}.go, each with that system's values and a permalink to the header that defines them. The build constraints of those four files together are exactly the constraint oflibcgo.go, so the constants are defined on every target that definessighow, once.libcgo_netbsd.gogets the values too. goffi does not support NetBSD yet (the module doesn't build for it onmain), but #88 adds it on top of this fakecgo and would otherwise ship the same bug.New test:
TestSighowMatchesSystemHeaderscompiles a tiny C probe with the system compiler and compares the three constants with the host's<signal.h>, so it doesn't just restate the numbers. It is skipped when no C compiler is available.CI used to run tests only for
./ffiand./types, so no test underinternal/ever ran in CI. This PR adds a separateRun internal package testsstep (go test ./internal/..., no coverage, so the coverage numbers don't change). The new test now runs against the real<signal.h>in the ubuntu and macOScgo=0jobs. The same step also runs the existinginternal/arch/amd64andinternal/arch/arm64tests in CI for the first time (arm64 on the macOS runner).Verification
Linux (WSL Ubuntu 22.04, Go 1.27.1):
TestSighowMatchesSystemHeaderspasses.libcgo_linux.go, the test fails withSIG_SETMASK = 3, but <signal.h> on this system says 2, so it catches this class of bug.go test -count=1 ./...passes withCGO_ENABLED=0andCGO_ENABLED=1, andgo vet ./...is clean.build-tags: [linux]line so each GOOS lints its own files: the findings are identical tomain, none of them ininternal/fakecgo. The existing findings on other targets are tracked separately.go build ./...for linux, darwin and freebsd (amd64 and arm64), android/arm64 and windows (amd64, arm64), plus-tags goffi_staticandCGO_ENABLED=1.internal/fakecgobuilds, vets and compiles its test binary for darwin, freebsd and netbsd (amd64 and arm64).Not verified yet:
macos-latestwill run the new test.macOS validation
@lkmavi, could you run this branch on your Mac?
The first should print
PASS(notSKIP). If you have time, any gogpu app you normally run on macOS built against this branch would be a good real-world check too.Release
Patch release (bug fix, no API change).