Skip to content

test(wasix): cover POSIX signal semantics (SA_SIGINFO, SA_RESETHAND, signal masks) - #6838

Merged
Arshia001 merged 5 commits into
mainfrom
test/sigaction-sa-siginfo-dispatch
Jul 30, 2026
Merged

test(wasix): cover POSIX signal semantics (SA_SIGINFO, SA_RESETHAND, signal masks)#6838
Arshia001 merged 5 commits into
mainfrom
test/sigaction-sa-siginfo-dispatch

Conversation

@Arshia001

@Arshia001 Arshia001 commented Jul 30, 2026

Copy link
Copy Markdown
Member

What

Seven fixtures under lib/wasix/tests/wasm_tests/signal/ covering POSIX signal semantics:

Fixture Covers
sigaction-siginfo Both dispatch forms: sa_handler (one argument) and sa_sigaction + SA_SIGINFO (three arguments)
sigaction-resethand SA_RESETHAND resets the disposition to SIG_DFL on entry
sigaction-mask-defers A blocked signal is deferred, not nested
sigaction-nodefer-reenters SA_NODEFER allows re-entry
sigaction-mask-blocks-other sa_mask blocks other named signals during a handler
sigprocmask-blocks-and-pends sigprocmask blocks, sigpending reports, unblocking delivers
pthread-sigmask-inherit A thread inherits its creator's mask, and the mask is per-thread

Why

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 mismatch rather than harmlessly ignoring the surplus arguments as native ABIs do. The runtime maps that to WasiError::Exit(Errno::Intr) — exit code 27 — killing the instance. wasix-libc's __wasm_signal did exactly that.

Fixing it exposed two more: SA_RESETHAND was 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_mask was stored but never applied, pthread_sigmask was a stub, so every handler ran as though SA_NODEFER were set.

All three are fixed in wasix-org/wasix-libc#128; these are the regression tests.

The mask pair

sigaction-mask-defers and sigaction-nodefer-reenters are the same program differing only in SA_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:

Fixture Expected
sigaction-mask-defers calls=5 max_depth=1 — deferred, handler runs sequentially
sigaction-nodefer-reenters calls=5 max_depth=5 — re-enters, nesting observable

The 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 as SA_NODEFER, so mask-defers is the fixture that proves the fix.

Two details worth knowing when reading them:

  • 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.
  • mask-defers re-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 give calls=2.

sigaction-resethand asserts the disposition reset directly via sigaction(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:

signal/sigaction-siginfo expected exit code 0, got 27
stdout: plain handler ok
error: WASI error: WASI exited with code: ExitCode::27

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.1 so 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

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>
Copilot AI review requested due to automatic review settings July 30, 2026 07:46

Copilot AI 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.

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-siginfo wasm_test that exercises both sa_handler (1-arg) and sa_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.

Comment thread lib/wasix/tests/wasm_tests/signal/sigaction-siginfo/main.c

@marxin marxin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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>
@Arshia001 Arshia001 changed the title test(wasix): cover sa_handler and SA_SIGINFO signal dispatch test(wasix): cover POSIX signal handler dispatch (SA_SIGINFO, SA_RESETHAND) Jul 30, 2026
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>
@Arshia001 Arshia001 changed the title test(wasix): cover POSIX signal handler dispatch (SA_SIGINFO, SA_RESETHAND) test(wasix): cover POSIX signal semantics (SA_SIGINFO, SA_RESETHAND, signal masks) Jul 30, 2026
Arshia001 and others added 2 commits July 30, 2026 13:35
`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>
@Arshia001
Arshia001 merged commit 48bad72 into main Jul 30, 2026
113 of 115 checks passed
@Arshia001
Arshia001 deleted the test/sigaction-sa-siginfo-dispatch branch July 30, 2026 16:18
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.

3 participants