Skip to content

dinit: preserve SIGCHLD reaping across Wasm SjLj#911

Closed
brandonpayton wants to merge 2 commits into
mainfrom
fix/dinit-siglongjmp-noexcept
Closed

dinit: preserve SIGCHLD reaping across Wasm SjLj#911
brandonpayton wants to merge 2 commits into
mainfrom
fix/dinit-siglongjmp-noexcept

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Purpose

Keep Dinit's SIGCHLD event path from aborting when Dasynq resumes its pselect loop with siglongjmp under WebAssembly SjLj.

Root cause

Wasm SjLj represents siglongjmp with a dedicated exception transfer. Dasynq's pselect_events::pull_events(bool) noexcept contains the corresponding sigsetjmp landing, but Clang also emits a nearer catch-all termination region for the noexcept boundary. The signal handler's jump reaches that termination region first, calls std::terminate, and exits with status 134 before Dinit can reap the completed scripted service.

Raw clang-linked and fork-instrumented controls fail identically. This is a C++/Wasm SjLj portability boundary, not a fork-instrumentation failure and not a Kandelo signal or process-state shortcut.

What changes

  • Apply one fail-closed Dasynq portability patch that removes noexcept only from the affected pselect polling boundary.
  • Compile Dinit supervisor and client targets with -fwasm-exceptions, using the declared Nix host compiler for native generators.
  • Advance Dinit from revision 3 to revision 5 for the two byte-changing EH/SjLj layers already represented as revisions 4 and 5 in the ABI 39 batch.
  • Add raw and instrumented negative controls, a positive SIGCHLD/pselect/reap fixture, real Dinit scripted-service coverage, and a main-compatible dinitctl exception test.
  • Keep the deliberately uninstrumented control under local-binaries/test-fixtures, outside the publishable program artifact namespace.
  • Document the noexcept/SjLj ordering boundary in the SDK guide.

Validation

All build and test commands ran through scripts/dev-shell.sh.

  • Full scripts/build-programs.sh: passed.
  • Focused Dinit Vitest: 2 files, 7 tests passed.
  • Raw and instrumented noexcept controls: both entered the handler, exited 134 with libc++abi: terminating, and did not reach the landing.
  • Non-noexcept fixture: resumed the same SjLj tag and exited 0.
  • Real Dinit scripted service: child reaped, [ OK ] child observed, then the known Dinit PID was terminated and awaited before host teardown.
  • Main-compatible dinitctl: expected exit 1 and missing-socket stderr, with no Wasm exception or libc++ termination.
  • Canonical test-workspace pack/unpack: raw and instrumented fixture hashes remained byte-identical.
  • Host typecheck: passed.
  • xtask build-deps check and Dinit manifest parse: passed.
  • ABI snapshot/header/TypeScript consistency: passed.
  • Bash syntax and diff checks: passed.
  • Synthetic merge with current origin/main 2c083e143618e4974a637b649e6221c64f0dd936: clean and tree-identical.

No dedicated browser UI suite was run for this standalone package PR. The same revision-5 Dinit bytes are part of the ABI 39 batch's Node/browser integration path.

ABI and package impact

This changes Dinit package bytes and therefore advances its package revision. It does not change the Kandelo kernel/host ABI, syscall semantics, channel layout, memory layout, or fork instrumentation contract; ABI_VERSION and the ABI snapshot are unchanged.

ABI 39 batch relationship

#907 head 0f464442db59397fab7098ae154d7e9640cc345c already carries the same semantic Dinit repair in commits 085371b1 and 0f464442, plus an ABI-39-only host-diagnostics assertion. This dedicated PR is the focused review vehicle with stronger raw/instrumented negative proof.

Do not mechanically merge this PR ahead of #907 while assuming the locked batch remains conflict-free: the two branches intentionally overlap in the Dinit build, patch, and test files. The batch should retain its ABI-39-only diagnostic delta and treat this reviewed tree as the provenance for the shared repair.

Wasm SjLj represents siglongjmp as an exception transfer. Dasynq's
noexcept pselect boundary intercepts that transfer in a nearer termination
region before pull_events can resume its local sigsetjmp landing pad, so
Dinit aborts while reaping a completed scripted service.

Remove only that incompatible portability boundary, compile Dinit with the
Wasm exception model it requires, and advance the package revision for the
changed artifacts. Add raw and fork-instrumented negative controls, a
positive SIGCHLD/reap fixture, real Dinit supervision coverage, and a
main-compatible dinitctl exception regression. The raw module remains a
test-only packed fixture, never a publishable program artifact.

This changes Dinit package bytes but does not change the Kandelo ABI.
@brandonpayton

Copy link
Copy Markdown
Member Author

Independent devil review: ACCEPT exact head d2cf206a654e1804b59ee1960473f608a6df0f5f, tree 44d242d5ccc33daa9df4f3f91c431e805901e316, on current main 2c083e143618e4974a637b649e6221c64f0dd936.

The reviewer covered the full pre-squash range, reran the canonical program build, 7/7 focused tests, host typecheck, ABI consistency, syntax/diff checks, raw/instrumented structure, CI workspace packing, PID-aware teardown, and a tree-identical synthetic merge. The accepted two-commit candidate was then squashed without changing its reviewed tree.

No code finding remains. The operational overlap with #907 is real and is documented in the PR body; this PR must not be merged ahead of the locked batch under an assumption of a conflict-free mechanical merge.

@github-actions

Copy link
Copy Markdown

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

ABI v18. 6 built, 0 failed, 6 total.

Package Arch Status Sha
dinit wasm32 built d4cfb9b0
lamp wasm32 built 2f0f1075
mariadb-test wasm32 built 0c3317cf
mariadb-vfs wasm32 built 82f551c4
mariadb-vfs wasm64 built 46f39a72
wordpress wasm32 built 039a8dba

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

@brandonpayton

Copy link
Copy Markdown
Member Author

CI follow-up at c6737de51: the Dinit case did not hit its own 10-second child-reap timeout. Vitest killed the enclosing test at its inherited 5-second default first, so the intended diagnostic/teardown window was structurally unreachable under a sufficiently loaded runner.

The test now has an explicit 30-second outer deadline covering startup, the existing 10-second behavioral guard, and cleanup. No Dinit/runtime behavior or artifact input changed. The exact focused command passed 6/6 tests, with the real scripted-service case completing in 834 ms:

./scripts/dev-shell.sh npx --prefix host vitest run --root . packages/registry/dinit/test/dinit-sigchld-sjlj.test.ts

The same old run also failed host/test/teardown-reclaim.test.ts:71; that is the independently isolated teardown-readiness race fixed by open PR #889, not a Dinit/SjLj regression. Every staged Dinit/downstream package and the browser, kernel, fork-instrument, libc, and POSIX suites passed on the old head.

@brandonpayton

brandonpayton commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Hosted rerun classification at exact head c6737de513242777f826bd2cd179cc5a7faee1b5:

  • all six staged Dinit/downstream package builds passed;
  • dinit-sigchld-sjlj.test.ts passed 6/6 under the new outer deadline (7.551 s on the loaded runner);
  • dinitctl.test.ts passed;
  • browser, cargo-kernel, fork-instrument, and POSIX passed in this run;
  • the sole completed-suite failure is again host/test/teardown-reclaim.test.ts:71, with the exact missing cooperative-exit observation fixed independently by Make teardown reclamation CI wait for a blocked worker #889.

This run confirms the Dinit timeout correction made its intended behavioral deadline reachable. It does not introduce a new Dinit failure; the red Vitest status is the already isolated teardown-readiness race.

@brandonpayton

Copy link
Copy Markdown
Member Author

Merged PR #907 supersedes this work through the strengthened Dinit lifecycle fixes now on main at dc2366a94 and 80f281c91. This head was reconstructed rather than merged verbatim.

@brandonpayton

Copy link
Copy Markdown
Member Author

Post-#907 cleanup audit: recording the still-current branch head c6737de513242777f826bd2cd179cc5a7faee1b5 before extracting the maintained test/docs residual onto current main. The replacement will link issue #918 and will not replay the Dinit package patch, package revision changes, or production changes already on main.

@brandonpayton

Copy link
Copy Markdown
Member Author

The useful test and documentation follow-up is now #925, with the underlying LLVM limitation still tracked in #918. #907 already landed the Dinit production and package fix. #925 deliberately keeps only generic raw and fork-instrumented controls, the positive SIGCHLD fixture, Node and Chromium coverage including wasm64, and SDK documentation; it does not replay Dinit patches or package revisions. The old remote head was c6737de.

@brandonpayton
brandonpayton deleted the fix/dinit-siglongjmp-noexcept branch July 13, 2026 18:19
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Add raw wasm32/wasm64 and fork-instrumented controls for issue #918, plus a positive SIGCHLD child-reaping fixture. Cover the behavior in Node and Chromium and document the pinned LLVM 21 limitation without replaying Dinit production or package changes from #911.
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Add raw wasm32/wasm64 and fork-instrumented controls for issue #918, plus a positive SIGCHLD child-reaping fixture. Cover the behavior in Node and Chromium and document the pinned LLVM 21 limitation without replaying Dinit production or package changes from #911.
brandonpayton added a commit that referenced this pull request Jul 14, 2026
Add raw wasm32/wasm64 and fork-instrumented controls for issue #918, plus a positive SIGCHLD child-reaping fixture. Cover the behavior in Node and Chromium and document the pinned LLVM 21 limitation without replaying Dinit production or package changes from #911.
brandonpayton added a commit that referenced this pull request Jul 16, 2026
Add raw wasm32/wasm64 and fork-instrumented controls for issue #918, plus a positive SIGCHLD child-reaping fixture. Cover the behavior in Node and Chromium and document the pinned LLVM 21 limitation without replaying Dinit production or package changes from #911.
brandonpayton added a commit that referenced this pull request Jul 16, 2026
Add raw wasm32/wasm64 and fork-instrumented controls for issue #918, plus a positive SIGCHLD child-reaping fixture. Cover the behavior in Node and Chromium and document the pinned LLVM 21 limitation without replaying Dinit production or package changes from #911.
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