Skip to content

internal/fakecgo: pthread_sigmask uses Linux constants on Darwin/FreeBSD/NetBSD (SIG_SETMASK unblocks all signals) #552

Closed

Description

@ukai

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 = 1
  • SIG_UNBLOCK = 2
  • SIG_SETMASK = 3
    (For reference, see <sys/signal.h> on Darwin/FreeBSD/NetBSD, or the Go runtime definitions in src/runtime/os_darwin.go, src/runtime/os_freebsd.go, and src/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:

  1. Instead of temporarily blocking all signals in ign across pthread_create, all signals are unblocked on the calling thread, and the newly spawned thread inherits an all-unblocked signal mask.
  2. After pthread_create, pthread_sigmask(SIG_SETMASK /* 2 = SIG_UNBLOCK */, &oset, nil) unblocks oset rather 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):

  1. A newly spawned OS thread begins executing threadentry with sigaltstack disabled (SS_DISABLE, the OS default for a new pthread before Go sets up its signal stack).
  2. threadentry calls setg_trampoline(...), installing the thread's g0 pointer into TLS. At this moment, ts.g.stackhi is set to the stack byte size (e.g., 0x80000) and ts.g.stacklo is 0, because runtime.mstart0 has not yet had a chance to set g0.stack to the actual OS stack bounds.
  3. If SIGPROF (signal 27) arrives on the new thread in the window between setg_trampoline and mstart0, it is delivered on the thread's normal stack (since signals were never masked, and sigaltstack is disabled).
  4. Darwin's signal trampoline runtime.sigtramp runs, loads the non-nil g0 from TLS, and calls runtime.sigtrampgo.
  5. runtime.sigtrampgo observes that sigaltstack is SS_DISABLE and checks if the current stack pointer sp is within g0.stack ([0, stackhi]). Since sp is in the thread's real virtual memory space (e.g. 0x16... or 0x7f...), this check fails.
  6. The Go runtime assumes an external non-Go thread disabled sigaltstack and immediately crashes:
    signal 27 received on thread with no signal stack
    fatal error: non-Go code disabled sigaltstack
    
    (This is the root cause behind runtime: signal 27 received on thread with no signal stack golang/go#77588).

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

  1. added
    bugSomething isn't working
    on Oct 15, 2026
  2. added this to the v0.11.2 milestone on Oct 15, 2026
  3. TotallyGamerJet commented on Oct 15, 2026

    @TotallyGamerJet
    Collaborator

    Seems like this should be cherry picked

  4. added a commit that references this issue on Oct 15, 2026
    0fd9f62
  5. hajimehoshi commented on Oct 15, 2026

    @hajimehoshi
    Member

    I've cherry-picked the fix to 0.11.

    @ukai Please use 0fd9f62f2e6b53025cd8bb5c29d98350de5c6e77 until I add a tag v0.11.2 a few days later. CC @shirou for gopsutil

  6. added a commit that references this issue on Oct 17, 2026
    d456b1b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions