Skip to content

runtime: SIGSEGV on child side of fork pre-exec with -race on darwin/arm64 #79804

@SebTardif

Description

@SebTardif

Go version

go1.26.4 darwin/arm64 (GOTOOLCHAIN resolves to go1.26.3 from Homebrew)

Output of go env in your module/workspace

AR='ar'
CC='cc'
CGO_ENABLED='1'
GOARCH='arm64'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOOS='darwin'
GOROOT='/opt/homebrew/Cellar/go/1.26.3/libexec'
GOVERSION='go1.26.3'

What did you do?

Ran concurrent Go tests that spawn child processes via os/exec while net/http/httptest TLS servers are active, with -race enabled on darwin/arm64.

Minimal reproducer (no third-party dependencies, pure stdlib):

// repro_test.go
package repro

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os/exec"
	"testing"
	_ "crypto/x509"
	_ "net"
)

func makeTest(i int) func(t *testing.T) {
	return func(t *testing.T) {
		t.Parallel()
		srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, `{"id": %d}`, i)
		}))
		defer srv.Close()
		client := srv.Client()
		for k := 0; k < 5; k++ {
			resp, _ := client.Get(srv.URL)
			if resp != nil {
				io.ReadAll(resp.Body)
				resp.Body.Close()
			}
		}
		for j := 0; j < 30; j++ {
			cmd := exec.Command("go", "version")
			if out, err := cmd.CombinedOutput(); err != nil {
				t.Errorf("exec %d: %v (%s)", j, err, out)
				return
			}
		}
	}
}

func TestP00(t *testing.T) { makeTest(0)(t) }
func TestP01(t *testing.T) { makeTest(1)(t) }
func TestP02(t *testing.T) { makeTest(2)(t) }
func TestP03(t *testing.T) { makeTest(3)(t) }
func TestP04(t *testing.T) { makeTest(4)(t) }
func TestP05(t *testing.T) { makeTest(5)(t) }
func TestP06(t *testing.T) { makeTest(6)(t) }
func TestP07(t *testing.T) { makeTest(7)(t) }
func TestP08(t *testing.T) { makeTest(8)(t) }
func TestP09(t *testing.T) { makeTest(9)(t) }

To reproduce, create 10 copies of this file in separate packages (pkg01/ through pkg10/, adjusting the package name) and run:

go test -count=5 -race -parallel=100 ./pkg*/

The crash is intermittent; it occurs roughly 1 in 10 runs.

What did you expect to see?

All tests pass.

What did you see instead?

Intermittent SIGSEGV:

--- FAIL: TestP08 (3.12s)
    repro_test.go:54: exec 26 failed: signal: segmentation fault (output: )
FAIL

macOS generates a crash report with this key annotation:

"asi": {"libsystem_c.dylib": ["crashed on child side of fork pre-exec"]}
"exception": {"type": "EXC_BAD_ACCESS", "signal": "SIGSEGV", "subtype": "KERN_INVALID_ADDRESS at 0x0000000000000008"}

The crash occurs in the forked child process before exec() replaces it, i.e. it is in the Go runtime's fork/exec path, not in the child binary.

Additional observations

  • Requires -race: 0 crashes in 10 runs without -race; ~2 in 5 with -race.
  • MallocNanoZone=0 does not help: This is not the same issue as runtime: race detector SIGABRT or SIGSEGV on macOS Monterey #49138.
  • darwin/arm64 specific: macOS 26.5.1 (25F80), Apple Silicon (Mac17,8).
  • Crash address is deterministic: Always 0x8 (nil pointer dereference at struct offset 8). The instructionByteStream at the crash point is identical across different test binaries and runs.
  • Binary size does not matter: The crash reproduces with both a small stdlib-only test binary (~12MB) and a larger one with third-party dependencies (~40MB).
Full macOS crash report (.ips)
{"app_name":"pkg02.test","timestamp":"2026-06-03 06:25:22.00 -0700","app_version":"","slice_uuid":"...","build_version":"","platform":1,"bug_type":"309","os_version":"macOS 26.5.1 (25F80)","name":"pkg02.test"}
{
  "cpuType": "ARM-64",
  "procName": "pkg02.test",
  "parentProc": "pkg02.test",
  "exception": {
    "type": "EXC_BAD_ACCESS",
    "signal": "SIGSEGV",
    "subtype": "KERN_INVALID_ADDRESS at 0x0000000000000008"
  },
  "termination": {"indicator": "Segmentation fault: 11"},
  "asi": {"libsystem_c.dylib": ["crashed on child side of fork pre-exec"]},
  "faultingThread": 0
}

Metadata

Metadata

Assignees

Labels

FixPendingIssues that have a fix which has not yet been reviewed or submitted.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type
No fields configured for issues without a type.

Projects

Status
In Progress

Relationships

None yet

Development

No branches or pull requests

Issue actions