Skip to content

fix(amd64): keep the args pointer of syscallN inside its frame - #109

Merged
kolkov merged 2 commits into
go-webgpu:mainfrom
aardwulf89:fix/syscalln-args-slot
Oct 24, 2026
Merged

kolkov merged 2 commits into
go-webgpu:mainfrom
aardwulf89:fix/syscalln-args-slot

Conversation

@aardwulf89

Copy link
Copy Markdown
Contributor

Fixes #108.

syscallN in internal/syscall/syscall_unix_amd64.s saved its args pointer at PTR_ADDRESS(BP), 72 bytes above the saved BP. That slot is outside the frame of syscallN, on the g0 stack, and it overwrote the return address of runtime.mstart0. When a goroutine exited while locked to its thread after a goffi call, mstart0 returned through that slot and the process crashed with SIGSEGV. #108 has the details and a reproduction.

Changes

  • internal/syscall/syscall_unix_amd64.s: save and load the pointer at PTR_ADDRESS(SP) (lines 55 and 108). This matches the frame layout comment of the file and purego's syscallX.
  • ffi/locked_exit_test.go: TestCallThenLockedExit makes a call to getpid in each of 16 goroutines per round, for 10 rounds, and lets each goroutine exit with its thread locked. Without the fix the test binary crashes with SIGSEGV under both CGO_ENABLED=0 and CGO_ENABLED=1. With the fix it passes.

Checks

On linux/amd64, go1.27.3:

  • go fmt ./...
  • golangci-lint run --config=.golangci.yml ./...: 0 issues
  • go test -race ./... (CGO_ENABLED=1) and CGO_ENABLED=0 go test ./...: pass
  • go test -bench=BenchmarkGoffi -benchmem ./ffi: BenchmarkGoffiIntArgs 72.8 ns/op, no change expected (the instruction count is the same)
  • scripts/check-spdx.sh: OK
  • darwin/amd64 and freebsd/amd64: not tested locally. They use the same file, and the test runs there.

🤖 Generated with Claude Code

syscallN saved the args pointer at PTR_ADDRESS(BP), 72 bytes above the
saved BP. That slot is outside the frame of syscallN, on the g0 stack,
and it overwrote the return address of runtime.mstart0. When a
goroutine exited while locked to its thread after a call, mstart0
returned through that slot and the process crashed with SIGSEGV.

Save and load the pointer at PTR_ADDRESS(SP), as the frame layout
comment states and as purego's syscallX does.

TestCallThenLockedExit makes a call in each goroutine and then exits
with the thread locked. It crashes without the fix.

Fixes go-webgpu#108

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@aardwulf89
aardwulf89 requested a review from kolkov as a code owner October 23, 2026 17:01
@kolkov kolkov added this to the v0.6.8 milestone Oct 24, 2026
@kolkov kolkov added type: bug Something isn't working priority: high Important for next release status: review In code review area: assembly Platform-specific assembly (ABI) area: ffi Public API (PrepareCallInterface, CallFunction) arch: amd64 x86-64 specific (System V / Win64 ABI) platform: linux Linux-specific platform: macos macOS-specific (includes Apple Silicon) platform: freebsd FreeBSD-specific labels Oct 24, 2026
@codecov

codecov Bot commented Oct 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @aardwulf89, good catch, and a clean fix. PTR_ADDRESS(BP) landed 72 bytes above the saved BP, in the caller's frame on g0. The frame comment already said SP+72.

What I checked:

  • linux/amd64 (WSL, go1.27.1): with main's syscall_unix_amd64.s and your test, the test binary dies with signal: segmentation fault under both CGO_ENABLED=0 and CGO_ENABLED=1. With the fix, -count=3 passes for both.
  • Full suite on the fix: go test ./... with cgo 0 and 1, and -race -p 1 with cgo=1, all pass. CI is green on all 49 checks, and the CI logs show TestCallThenLockedExit running (not skipped) on linux amd64/arm64, both macOS runners and the four FreeBSD VMs.
  • arm64 keeps the pointer inside its own frame (80(RSP) in syscall_arm64.s), and Windows amd64 goes through syscall.SyscallN, so only the Unix amd64 path had this.

I added a CHANGELOG entry crediting you (6bc8870). Approving.

@kolkov
kolkov merged commit 48fe366 into go-webgpu:main Oct 24, 2026
49 checks passed
@kolkov kolkov mentioned this pull request Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch: amd64 x86-64 specific (System V / Win64 ABI) area: assembly Platform-specific assembly (ABI) area: ffi Public API (PrepareCallInterface, CallFunction) platform: freebsd FreeBSD-specific platform: linux Linux-specific platform: macos macOS-specific (includes Apple Silicon) priority: high Important for next release status: review In code review type: bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

amd64: syscallN stores its args pointer outside its frame, so a locked thread that exits after a call crashes (SIGSEGV)

2 participants