Skip to content

wasix: reject IPC descriptor passing up front with ENOSYS - #14

Open
Arshia001 wants to merge 1 commit into
wasix-1.51.0from
fix/wasix-ipc-handle-enosys
Open

wasix: reject IPC descriptor passing up front with ENOSYS#14
Arshia001 wants to merge 1 commit into
wasix-1.51.0from
fix/wasix-ipc-handle-enosys

Conversation

@Arshia001

Copy link
Copy Markdown

Problem

On WASIX, child.send(message, handle) reports success while the message is silently dropped, and both peers then wait forever.

WASI has no msghdr/SCM_RIGHTS, so uv__try_write() already refuses a write carrying a descriptor:

if (send_handle != NULL) {
#ifndef __wasi__
    ... sendmsg ...
#else
    /* WASI does not support msghdr. */
    return UV_ENOSYS;
#endif

But that refusal happens inside the queued write, so uv_write2() has already returned 0 and the failure is delivered only as a write-completion status. Node's IPC channel discards that status — lib/internal/child_process.js installs:

req.oncomplete = () => {
  control.unrefCounted();
  if (typeof callback === 'function') callback(null);   // status ignored
};

So the sender is told the write succeeded, nothing goes out, and the receiver waits for a message that can never arrive.

Fix

Reject in uv__check_before_write() so uv_write2() fails synchronously and the caller sees ENOSYS. This mirrors the Cygwin/MSYS arm immediately above, which returns ENOSYS for exactly the same reason (cannot pass descriptors).

Only descriptor passing changes. IPC byte streams are untouched, so plain process.send() and the cluster control protocol keep working.

Verification

Built into the edgejs WASIX guest and exercised through the Node test suite:

  • node:dgram category: 60 passed, no regressions (the one pre-existing failure, known_issues/test-dgram-bind-shared-ports-after-port-0, is unrelated — it needs real SCM_RIGHTS fd passing and cannot pass on WASIX either way).
  • Plain IPC still works in both directions (parent->child and child->parent round-trip verified).
  • A cluster shared-socket bind now surfaces a prompt ENOSYS on the primary instead of stalling silently.

Before / after for a handle-passing send:

before:  P: send-cb ok          <- false success, message dropped
after:   P: send-cb ENOSYS / write ENOSYS

This does not make descriptor passing work — that needs SCM_RIGHTS support in WASIX. It makes the unsupported case fail honestly and promptly instead of hanging.

WASI has no msghdr/SCM_RIGHTS, so uv__try_write() cannot attach a
descriptor to an IPC write and refuses it. That refusal happens inside
the queued write, and its status reaches the caller only as a write
completion. Node's IPC channel discards that status -- it installs
`req.oncomplete = () => callback(null)` -- so a `child.send(msg, handle)`
reports success while the message is dropped. Neither peer learns
anything: the sender believes it sent, the receiver waits forever.

Reject in uv__check_before_write() instead, so uv_write2() fails
synchronously and callers see ENOSYS. This mirrors the Cygwin/MSYS arm
directly above, which returns ENOSYS for the same reason.

Only descriptor passing is affected; IPC byte streams keep working, so
plain process.send() and the cluster control protocol are unchanged.
Verified on the edgejs WASIX lane: the node:dgram category is unchanged
at 60 passed, and a cluster shared-socket bind now surfaces a prompt
ENOSYS on the primary instead of stalling silently.
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.

2 participants