Skip to content

[PHP] fix: preserve process-shared state across fork and exec#756

Merged
brandonpayton merged 19 commits into
integration/kd-6nz-php-phpt-platform-fixesfrom
gascity/kd-6nz/kd-e0l-pr717-fork-process-shm-split
Jul 11, 2026
Merged

[PHP] fix: preserve process-shared state across fork and exec#756
brandonpayton merged 19 commits into
integration/kd-6nz-php-phpt-platform-fixesfrom
gascity/kd-6nz/kd-e0l-pr717-fork-process-shm-split

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jun 20, 2026

Copy link
Copy Markdown
Member

Purpose

Preserve authoritative process, descriptor, and shared-memory state across
fork() and exec() so PHP-FPM and other multi-process POSIX software use the
normal Kandelo platform path.

This replaces the stale split branch's eight unique commits on an altered
socket integration base with nineteen reviewed, append-only repair commits on
the exact Batch 2 aggregate tip (e45019127). It intentionally does not replay
the altered socket base wholesale.

Reviewed result

  • Distinguishes normal exit codes from signal termination and preserves Rust's
    zombie/wait authority through host worker lifecycle transitions.
  • Corrects mmap placement, rounded unmap geometry, PID allocation, and
    fallible exec preparation before destructive state changes.
  • Commits exec in place against the authoritative process, preserving surviving
    OFDs and kernel-backed objects while closing CLOEXEC descriptors and resetting
    image-specific state.
  • Shares AF_UNIX accept queues, anonymous/SysV and regular-file MAP_SHARED
    backings, and refcounted eventfd/timerfd/signalfd/memfd/procfs state across
    fork/spawn/exec at the correct ownership boundary.
  • Coordinates direct fork from instrumented side modules with versioned,
    transactional table/archive replay and rollback.
  • Preserves signal, epoll, thread-worker, and sleep state at process lifecycle
    boundaries. Host-originated shared signals select the exact eligible TID;
    ignored signals do not shorten sleeps, and no specialized host dispatch
    resumes guest code after signal death.
  • Documents the supported process-sharing semantics and the remaining
    WebAssembly/host boundaries.
  • Regenerates the ABI snapshot/bindings for additive kernel exports and declares
    the exit-signal query optional for compatible ABI-16 hosts.

Review changes

The review rebuilt the constituent rather than force-fitting its stale socket
ancestry. The nineteen commits repair:

  1. wait status, mmap/unmap geometry, PID collision avoidance, and exec growth;
  2. AF_UNIX fork sharing and in-place exec state preservation;
  3. anonymous/SysV and regular-file shared mappings, including stable backend
    identity, dirty/version merging, direct-I/O coherence, partial unmaps,
    MAP_FIXED preflight, and exact lifecycle publication;
  4. shared in-kernel descriptor backings and side-module fork coordination;
  5. epoll, worker/thread registry, anonymous-mapping detach, and concurrent
    pthread sleep correctness;
  6. ABI-16 optional-export metadata and inactive-coherence fast paths; and
  7. exact-thread signal routing plus terminal post-dispatch boundaries and
    re-entrant address-space teardown guards.

Independent final audits found no remaining constituent blocker.

Review view

Accept into quarantined Batch 2 via rebase merge. Do not land this PR directly
to main.
The repaired constituent is coherent on the current #871 tip and
keeps its changes linearly revertible, but Batch 2 still needs the single atomic
ABI 16→17 reconciliation for the two divergent ABI-16 epochs and rebuilt PHP /
opcache artifacts.

Known boundaries remain explicit:

  • file MAP_SHARED coherence occurs at mediated syscall/lifecycle boundaries,
    not as immediate shared physical memory;
  • cross-process futex synchronization is not implemented;
  • OPFS backends without stable inode identity and shared memfd mappings return
    ENOTSUP;
  • beyond-EOF mapped pages are clipped/zero-filled rather than raising SIGBUS;
  • unmediated external host writers are not detected; and
  • the side-module role marker remains optional only for ABI 16 and becomes
    mandatory with the aggregate ABI-17 bump.

Validation

All claimed commands ran through scripts/dev-shell.sh.

Evidence Result
cargo test -p kandelo --target aarch64-apple-darwin --lib 1,066/1,066 passed at Rust/ABI tip 4987bae1d; no Rust/ABI/generated file changed afterward
cargo test -p fork-instrument --target aarch64-apple-darwin 183/183 passed
cargo test -p wasm-posix-shared --target aarch64-apple-darwin 19/19 passed
focused dump_abi tests 10/10 passed
bash scripts/check-abi-version.sh at 953d8afb9 vs e45019127 passed; one optional host export plus eight kernel exports are additive-compatible; ABI remains 16; snapshot/C/TS outputs synchronized
cd host && npx vitest run at 953d8afb9 108 files passed, 30 skipped; 940 tests passed, 2 expected failures, 166 skipped; zero failures
seven focused signal/process/mmap files 148 passed, 2 expected failures, 8 skipped
cd host && npm run build ESM, CJS, and declaration bundles built
npx tsx examples/run-example.ts examples/mmap_shared_anonymous_fork.wasm both coherence markers and PASS

Final nine-round microbenchmarks used identical benchmark artifacts on the
candidate and exact e45019127 base:

Host / suite Base Candidate 953d8afb9
Node pipe/write/read MB/s; getpid us 22.31 / 154.82 / 229.41 / 11.71 22.57 / 152.67 / 225.89 / 10.40
Node hello/fork/exec/clone/spawn ms 152.00 / 44.40 / 194.40 / 40.76 / 42.85 155.64 / 43.86 / 195.87 / 40.82 / 43.02
Chromium pipe/write/read MB/s; getpid us 90.91 / 200 / 250 / 11 83.33 / 166.67 / 250 / 12
Chromium hello/fork/clone/spawn ms 93.21 / 9 / 6 / 8 115.56 / 10 / 7 / 9

The Node medians are close. Chromium uses coarse buckets and its candidate
hello rounds ranged from 80.98 to 171.31 ms, so these data are not presented as
a performance-neutrality claim.

Per the locked Batch 2 policy, this constituent did not run the full
ready-to-merge libc/POSIX/PHP/PHPT/application gate, manual ./run.sh browser
verification, or the full PHP browser PHPT suite. Those run once on the exact
ABI-17 aggregate. The isolated PHP image is not final-runnable here because
the harness interrupt import is supplied by #757 and final package artifacts
must be rebuilt for ABI 17.

@github-actions

Copy link
Copy Markdown

Phase B-1 matrix build status — pr-756-staging

ABI v16. 63 built, 4 failed, 67 total.

Package Arch Status Sha
libcurl wasm32 built a6f6534d
libcxx wasm32 built 0d3c1977
libcxx wasm64 built 3117bdac
libiconv wasm32 built 1be46463
libiconv wasm64 built 82d9fdf3
libpng wasm32 built 697eac68
libxml2 wasm32 built 41fada20
libxml2 wasm64 built b3752126
openssl wasm32 built 96fbd5a6
openssl wasm64 built b53b8ff0
sqlite wasm32 built 10a86a4f
sqlite wasm64 built e74c37db
zlib wasm32 built 1b3d5c6f
zlib wasm64 built 8470f519
bc wasm32 built 645e81c1
bzip2 wasm32 built 85ec619a
coreutils wasm32 built d0c76ab8
curl wasm32 built 3dfec8e7
dash wasm32 built 04d2f6b8
diffutils wasm32 built c804830a
dinit wasm32 built e646ca69
fbdoom wasm32 built 935a71b0
file wasm32 built b9e73f9f
findutils wasm32 built aeeb6ecc
gawk wasm32 built 9c6d61e9
git wasm32 built ad9f9d06
grep wasm32 built c6aa7ff9
gzip wasm32 built 6c492249
kandelo-sdk wasm32 built fa69e4fc
kernel wasm32 failed
less wasm32 built 87aa211f
lsof wasm32 built c88ab5c9
m4 wasm32 built 5847a852
make wasm32 failed
mariadb wasm32 built d5d40cc7
mariadb wasm64 built 8c75bf16
modeset wasm32 built e5967428
msmtpd wasm32 built cff25a9b
nano wasm32 built d850deac
ncurses wasm32 built 9a77777f
netcat wasm32 built e8e76c42
nginx wasm32 built 3742a74e
php wasm32 built 6c215a1d
posix-utils-lite wasm32 built 3ab77df9
sed wasm32 built ef08c754
spidermonkey wasm32 built d1b3e35c
tar wasm32 built 7ac89a61
tcl wasm32 built 1e64df4d
unzip wasm32 built 3f650bbb
userspace wasm32 built 4dcfe62b
vim wasm32 built 9c5d375e
wget wasm32 built 2b0aaa25
xz wasm32 built dba87aba
zip wasm32 built f1c66da0
zstd wasm32 built 4381b467
bash wasm32 built 479f8133
mariadb-test wasm32 built 95e7de82
mariadb-vfs wasm32 built 9cca8b82
mariadb-vfs wasm64 built 97bcd722
nethack wasm32 built 6198392e
node wasm32 built cfe2bfc0
spidermonkey-node wasm32 built eee81883
vim-browser-bundle wasm32 built cb17bc3a
nethack-browser-bundle wasm32 built 0d6d8f1e
rootfs wasm32 failed
shell wasm32 failed
node-vfs wasm32 built f8a54c30

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

Merge anonymous MAP_SHARED and SysV writes against per-mapping snapshots at syscall boundaries, inherit their host backing across fork, and publish/detach them exactly once across exec, exit, and crash paths.

Keep Node, browser, and the main-thread test host in parity, including signal-style cleanup when a fork-child worker fails. File-backed mapping identity is handled separately.
Move eventfd, timerfd, signalfd, memfd, and procfs snapshot state into refcounted kernel-global backing tables so inherited open file descriptions keep one coherent identity across fork and spawn. Balance backing ownership through close, rollback, process removal, CLOFORK filtering, and retained legacy exec state while rejecting unreconstructable fresh-instance handles instead of aliasing stale slots.

Use the shared backing as the authoritative memfd/procfs cursor for reads, seeks, fdinfo, and SEEK_CUR locks. Keep host clock callbacks outside the backing lock and preserve existing fresh-kernel fork/exec behavior for ordinary reconstructable descriptors.
Emit versioned fork-instrument role claims, preserve an ABI-16 legacy fallback, and make ABI 17 activate mandatory side-entry and dlopen-main validation. Drive side-module unwind and rewind through an immutable main trampoline while persisting the active side buffer and exact table layout for child replay.

Reject statically visible nested side-module fork paths, roll failed loads back transactionally, retain first-definition symbol bindings, and document the remaining opaque-callback and pthread boundaries. Exercise repeated main-to-side fork and wait cycles with a real instrumented fixture.
Route host-originated shared signals to the exact eligible thread and rebind asynchronous channel completions before dequeueing. Consumed or ignored signals no longer shorten sleeps, and a kernel trap cannot trigger a false wake.

Treat process exit as terminal across specialized host-side syscall dispatches, including mmap writeback and synthetic memory operations. Guard address-space teardown against re-entrant signal cleanup so crash, exec-handoff, and clean-exit paths notify parents exactly once without resuming dead guest code.
@brandonpayton
brandonpayton force-pushed the gascity/kd-6nz/kd-e0l-pr717-fork-process-shm-split branch from 9a7ec18 to 953d8af Compare July 11, 2026 09:23
@brandonpayton brandonpayton changed the title [PHP] fix: split fork/process/shared-memory runtime fixes [PHP] fix: preserve process-shared state across fork and exec Jul 11, 2026
@brandonpayton
brandonpayton changed the base branch from integration/kd-6nz-php-phpt-socket-network-base to integration/kd-6nz-php-phpt-platform-fixes July 11, 2026 09:23
@brandonpayton
brandonpayton merged commit 23e70c5 into integration/kd-6nz-php-phpt-platform-fixes Jul 11, 2026
@brandonpayton
brandonpayton deleted the gascity/kd-6nz/kd-e0l-pr717-fork-process-shm-split branch July 11, 2026 09:24
brandonpayton added a commit that referenced this pull request Jul 11, 2026
Merge the address-sorted guest mapping and host reservation streams without materializing a combined list. Normalize fork-restored mappings once at their ingestion boundary so the ordering invariant is explicit.

Reference-equivalence tests cover interleaving, equal starts, overlap, exact fits, exhaustion, and systematic relative orderings. The copied munmap regression from the original PR remains owned by #756.

This is an unmeasured internal refactor. It makes no throughput or PHP/PHPT coverage claim and changes no intended host or ABI contract.
brandonpayton added a commit that referenced this pull request Jul 11, 2026
Merge the address-sorted guest mapping and host reservation streams without materializing a combined list. Normalize fork-restored mappings once at their ingestion boundary so the ordering invariant is explicit.

Reference-equivalence tests cover interleaving, equal starts, overlap, exact fits, exhaustion, and systematic relative orderings. The copied munmap regression from the original PR remains owned by #756.

This is an unmeasured internal refactor. It makes no throughput or PHP/PHPT coverage claim and changes no intended host or ABI contract.
brandonpayton added a commit that referenced this pull request Jul 12, 2026
Merge the address-sorted guest mapping and host reservation streams without materializing a combined list. Normalize fork-restored mappings once at their ingestion boundary so the ordering invariant is explicit.

Reference-equivalence tests cover interleaving, equal starts, overlap, exact fits, exhaustion, and systematic relative orderings. The copied munmap regression from the original PR remains owned by #756.

This is an unmeasured internal refactor. It makes no throughput or PHP/PHPT coverage claim and changes no intended host or ABI contract.
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Wait and signal results, mmap placement and ownership, exec cleanup, descriptor/socket/shared-memory inheritance, fork metadata, epoll, sleep, and signal-death behavior all change existing process contracts. Advance ABI 25 to 26 and regenerate ABI evidence atomically.

Require kernel_get_process_exit_signal so a missing kernel contract fails loudly. Retain file-backed MAP_SHARED handles across unlink, and drain last-owned host descriptors during forced process removal so the epoch is self-contained beneath its integration tests.

Source-PR: #756

Folded-Repair: 01907159db3a092d76cc695b948f58bf0b52d0d7

Folded-Reconstruction: required-export model coverage b1eb8f24d and the shared-memory XFAIL retirement c8cd117a6.
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Merge the address-sorted guest mapping and host reservation streams without materializing a combined list. Normalize fork-restored mappings once at their ingestion boundary so the ordering invariant is explicit.

Reference-equivalence tests cover interleaving, equal starts, overlap, exact fits, exhaustion, and systematic relative orderings. The copied munmap regression from the original PR remains owned by #756.

This is an unmeasured internal refactor. It makes no throughput or PHP/PHPT coverage claim and changes no intended host or ABI contract.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant