fork: fail truthfully when a fork continuation overruns its save buffer - #845
Closed
brandonpayton wants to merge 1 commit into
Closed
brandonpayton wants to merge 1 commit into
brandonpayton wants to merge 1 commit into
Conversation
This was referenced Jul 9, 2026
brandonpayton
force-pushed
the
fork-save-buffer-truthful-failure
branch
from
July 12, 2026 13:39
09a3514 to
0c5bd8b
Compare
The wpk_fork unwind writes the saved call stack into a fixed FORK_SAVE_BUFFER_SIZE (16 KiB) buffer that sits immediately below the syscall channel (forkBufAddr = channelOffset - FORK_BUF_SIZE). The instrumented unwind carries no bounds check of its own — crates/fork-instrument/src/runtime.rs documents the requirement `frames_start_offset + Sigma(frame) <= buffer_size` but never enforces it. So a fork() from a call stack too deep or wide to fit silently overruns the buffer into the channel, corrupting the syscall channel and later surfacing as an unexplained trap or a fork child that never makes progress — never as the real cause. Detect the overrun host-side, right after the unwind completes and before SYS_FORK is sent: current_pos (the write cursor the instrumentation keeps at the buffer base, which frames never clobber) exceeding the buffer size means the unwind wrote past it into the channel. When it does, fail the fork with a clear diagnostic naming the byte overage and the platform limit, instead of forking on a corrupted channel. Applied to both the main-process and thread fork paths, which Node and browser hosts share. Validated in a real browser by temporarily shrinking the buffer: a fork that overruns now reports "fork() continuation save buffer overflow ..." and dies cleanly, while forks that fit still succeed (no false positives). Adds a unit test for the detection arithmetic (wasm32 + wasm64); the existing fork/spawn/thread suites pass unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brandonpayton
force-pushed
the
fork-save-buffer-truthful-failure
branch
from
July 12, 2026 13:41
0c5bd8b to
f5ac952
Compare
Member
Author
|
Exact-head CI completed with every substantive suite green except the known fixed-delay race in
This PR's focused |
This was referenced Jul 12, 2026
Member
Author
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
Detect a fork-continuation save-buffer overrun immediately after unwind and
before
SYS_FORK, for both main-process and pthread fork paths. A too-deep ortoo-wide call stack now fails with the exact required and reserved byte counts
instead of spawning a child from channel-corrupting state.
The authoritative fork-instrumentation reference now documents that this is a
post-unwind detection boundary, not an in-Wasm prevention mechanism.
ABI 18 changed the frame cursor to an absolute linear-memory address. The
detector therefore compares
current_poswithforkBufAddr + FORK_SAVE_BUFFER_SIZE; comparing the absolute cursor directlywith the byte size would reject every valid ABI 18 unwind.
Root Cause Evidence
The exact ABI 18 Homebrew launcher reproduces this platform limit:
0x2ac000 .. 0x2b0000(16,384 bytes)0x2b0e2cWithout this PR, the channel activity zeroes the child's overrun
_startframe and rewind later traps in
__deliver_pending_signalat an indirect call.That symbol is downstream damage, not the cause. With this PR, the same real
brew --versionpath exits-1at the fork boundary and reports:Validation
./scripts/dev-shell.sh bash -c 'npm --prefix host exec vitest run test/fork-save-buffer-overrun.test.ts': 5/5 passed./scripts/dev-shell.sh bash -c 'npm --prefix host run typecheck': passedbrew --version: produced the 20,012/16,384 diagnostic and exited-1git diff --check origin/main...HEAD: passedFresh hosted Node/browser and conformance gates are required on this rebased
head.
Scope
This is truthful-failure hardening, not the functional capacity increase.
Homebrew remains blocked until the fork-save region is enlarged or made
elastic, which is an ABI layout change requiring a version bump and rebuilding
ABI-bound program artifacts. This PR makes that remaining boundary explicit
without changing the ABI or replacing any artifacts.