test(wasix): cover POSIX signal semantics (SA_SIGINFO, SA_RESETHAND, signal masks) - #6838
Merged
Merged
Conversation
Handlers installed through `sa_sigaction` with `SA_SIGINFO` take three arguments, while `sa_handler` handlers take one. On wasm the arity is part of the function type, so dispatching the three-argument form through the one-argument signature traps with "indirect call type mismatch" and exits with `Errno::Intr`. Exercise both dispatch forms. Requires the wasix-libc fix in wasix-org/wasix-libc; gated with `MinimalLibc` so pinned older sysroots skip rather than fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a WASIX regression test ensuring sigaction dispatch respects SA_SIGINFO so a 3-argument sa_sigaction handler is not invoked via the 1-argument sa_handler ABI (which would trap on wasm due to function type mismatch).
Changes:
- Add
signal/sigaction-siginfowasm_test that exercises bothsa_handler(1-arg) andsa_sigaction+SA_SIGINFO(3-arg) handler paths. - Pin the test behind a
//#MinimalLibc:gate so older sysroots skip the test until the libc fix is available.
marxin
approved these changes
Jul 30, 2026
marxin
left a comment
Collaborator
There was a problem hiding this comment.
LGTM - depends on not yet released wasix-libc, right?
POSIX resets a handler installed with SA_RESETHAND to SIG_DFL on entry, so a signal re-raised from inside it takes the default action rather than re-entering the handler. Node's `SignalExit` relies on that; without the reset it recurses until the stack is exhausted. The reset is asserted directly instead of by re-raising: unbounded re-entry exhausts the host stack and would take the test process down rather than failing this one test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five fixtures for sa_mask, SA_NODEFER, sigprocmask/sigpending, and mask inheritance across pthread_create. sigaction-mask-defers and sigaction-nodefer-reenters are the same program differing only in SA_NODEFER, so they pin the semantics from both sides: the handler re-raises its own signal and records how often it ran and how deep it nested. Without SA_NODEFER the delivery is deferred and the handler runs five times sequentially (max_depth 1); with it the handler re-enters and nests five deep. The re-raise is gated on a counter in guest code, so the recursion is bounded whatever libc does rather than exhausting the host stack. Note that nodefer-reenters must use an empty sa_mask: sa_mask is applied on top of SA_NODEFER, so a sigfillset() mask would block the signal anyway and defeat the opt-out being tested. Requires the mask implementation in wasix-org/wasix-libc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`make lint` runs clang-format over every .c file in the tree, and the repo style puts the pointer with the type (`siginfo_t* info`). All seven fixtures were written with the pointer on the identifier, so the Code lint job has been red since the first of them landed. Formatting only; the inline `//#Directive:` lines the harness parses are untouched and the suite still passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The new signal fixtures exercise SA_SIGINFO dispatch, SA_RESETHAND, and the signal mask, none of which exist in v2026-07-03.1. MinimalLibc only gates the explicitly-versioned sysroots in TESTED_LIBC_VERSIONS, so the configurations built against the pinned default run regardless and fail on the old libc -- sigaction-mask-defers exits 27 there and passes on v2026-07-30.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
Seven fixtures under
lib/wasix/tests/wasm_tests/signal/covering POSIX signal semantics:sigaction-siginfosa_handler(one argument) andsa_sigaction+SA_SIGINFO(three arguments)sigaction-resethandSA_RESETHANDresets the disposition toSIG_DFLon entrysigaction-mask-deferssigaction-nodefer-reentersSA_NODEFERallows re-entrysigaction-mask-blocks-othersa_maskblocks other named signals during a handlersigprocmask-blocks-and-pendssigprocmaskblocks,sigpendingreports, unblocking deliverspthread-sigmask-inheritWhy
On wasm the argument count is part of the function type, so a three-argument handler dispatched through the one-argument signature traps with
indirect call type mismatchrather than harmlessly ignoring the surplus arguments as native ABIs do. The runtime maps that toWasiError::Exit(Errno::Intr)— exit code 27 — killing the instance. wasix-libc's__wasm_signaldid exactly that.Fixing it exposed two more:
SA_RESETHANDwas never implemented (a handler re-raising its own signal re-entered itself until the host stack was exhausted), and there was no signal mask at all —sa_maskwas stored but never applied,pthread_sigmaskwas a stub, so every handler ran as thoughSA_NODEFERwere set.All three are fixed in wasix-org/wasix-libc#128; these are the regression tests.
The mask pair
sigaction-mask-defersandsigaction-nodefer-reentersare the same program differing only inSA_NODEFER, which pins the semantics from both sides. The handler re-raises its own signal and records how often it ran and how deep it nested:sigaction-mask-deferscalls=5 max_depth=1— deferred, handler runs sequentiallysigaction-nodefer-reenterscalls=5 max_depth=5— re-enters, nesting observableThe re-raise is gated on a counter in guest code, so recursion is bounded to 5 frames however libc behaves — no host stack exhaustion even with masking entirely broken. Against today's libc both report
max_depth=5, since every handler currently behaves asSA_NODEFER, somask-defersis the fixture that proves the fix.Two details worth knowing when reading them:
nodefer-reentersmust use an emptysa_mask:sa_maskis applied on top ofSA_NODEFER, so asigfillset()mask would block the signal anyway and defeat the opt-out being tested.mask-defersre-raises from inside each invocation rather than raising 5 times up front, because standard signals do not queue — several raises while blocked collapse into one pending delivery, which would legitimately givecalls=2.sigaction-resethandasserts the disposition reset directly viasigaction(sig, NULL, &old)rather than by re-raising, since unbounded re-entry would take the test process down instead of failing one test.Test status
Each fixture is red against a sysroot lacking the corresponding fix. For example
sigaction-siginfo:With sysroots built from wasix-org/wasix-libc#128 all pass, and the full suite is 1100 passed, 0 failed, 71 ignored.
Before merge
All seven carry
//#MinimalLibc: v2026-07-30.1so pinned older sysroots (v2026-05-12.1) skip rather than fail. That version is a placeholder — update it to the libc release that ships the fixes, and land this after that release is available.🤖 Generated with Claude Code