wasi: a Ctrl-C Interrupt record on guest stdio is treated as a protocol error and closes all three streams #28
Description
Finding (confirmed by reading; live in kore main's composition)
crates/wasi/src/stdio.rs's Shared::receive handles only Data and Closed records. Anything else falls to _ => lost = Protocol (stdio.rs:107). After that, all three guest streams return StreamError::Closed (stdio.rs:296-391), and the receive loop stops (while self.lost.is_none(), stdio.rs:88).
The Interrupt record is legal at any time per kore's process ABI (crates/abi/src/process.rs:166-174, 248-250 in kore). It is sent by:
- the terminal window (
crates/stream-host/src/lib.rs:579/617/647); - the shell's
interrupt_child(user/apps/shell/src/main.rs:1579).
User-visible result: Ctrl-C on wasm <guest> ends the guest with stdio lost: Protocol (user/apps/utils/wasm/src/main.rs:299 in kore). It does not deliver an interrupt.
Interaction with the parent's grace: reading the first interrupt record ends the parent's unread-interrupt grace (crates/rt/src/process/interrupt.rs:12-15 in kore). Because later records are never read, a third Ctrl-C, or 60 s after the second, still revokes.
kore main composes exactly this adapter. user/Cargo.toml pins kore-wasi from libkore, and its [patch] maps kore-abi to kore's in-tree ABI, which has carried Interrupt since kore a2866d761 (2026-09-29).
Suggested fix
- Decode
Interruptinreceiveand record a distinct interrupted state, notProtocol, that keeps any outstandingInputReady. - Connect that state to the guest's interrupt/cancellation policy, so the host can end the guest cleanly or surface the interrupt to it.
- Add a host test feeding an
Interruptrecord betweenDatarecords.
Surfaced by Copilot's review of #27 (that PR only re-exports native/; this adapter is unchanged there).
Activity
lua: native modules come only from the READ-only libs grant (#27)