Route sendmsg/recvmsg via shared host_iov helper

Merged
JS
5mo ago
+138-79

sys_sendmsg and sys_recvmsg resolved each guest iov entry with guest_ptr_bound() and clamped the entry's length to the contiguous prefix when the guest VA crossed a host-side discontinuity (primary slab to guest_mapping / guest_overflow region, or between two distinct mappings). The loop then continued populating host_iov[i+1..] at full requested length, so the host sendmsg(2) placed bytes from iov[i+1] on the wire where the truncated tail of iov[i] belonged, and recvmsg(2) scattered received bytes into the wrong guest buffer past a truncated entry. The case only fires when a guest iov entry straddles two distinct host mappings; the typical single-region iov masked it in tests.

Hoist the already-correct build_host_iov helper from src/syscall/io.c into src/syscall/internal.h as host_iov_prepare and the wrapper host_iov_prepare_msg, together with host_iov_buf_t and the SYSCALL_IOV_MAX / SYSCALL_IOV_STACK_MAX constants. host_iov_prepare keeps the strict readv/writev contract (rejects iovcnt <= 0); host_iov_prepare_msg short-circuits iovcnt == 0 for ancillary-only sendmsg/recvmsg payloads. Replace the duplicated inline iov build loops in src/syscall/net-msg.c with the shared call. Side effects: lifts the per-call iovcnt cap from 64 to SYSCALL_IOV_MAX (1024) and drops roughly 80 lines of duplicated logic.

The shared helper truncates at the first non-contiguous iov entry and zeros every subsequent entry, so the host call returns a POSIX-compliant short I/O instead of mis-aligning the wire data.


Summary by cubic

Fixes misaligned I/O in sendmsg/recvmsg when a guest iovec crosses non‑contiguous host mappings by routing both calls through a shared host_iov helper. Lifts the iov cap to 1024 and removes duplicated logic.

  • Bug Fixes

    • Truncate at the first non‑contiguous iov entry and zero the rest so the host returns a POSIX short I/O instead of misplacing data.
    • Cap msg_iovlen at SYSCALL_IOV_MAX before int narrowing; allow iovcnt == 0 for ancillary‑only messages.
  • Refactors

    • Hoisted build_host_iov into host_iov_prepare/host_iov_prepare_msg in src/syscall/internal.h with host_iov_buf_t, host_iov_free, and shared SYSCALL_IOV_MAX/SYSCALL_IOV_STACK_MAX.
    • Replaced inline iov loops in src/syscall/net-msg.c; raised per‑call cap from 64 to 1024; removed ~80 lines.
    • Added a test to keep strict readv/writev/pwritev2 behavior: zero iovcnt stays -EINVAL and does not move the file offset.