Skip to content

fix(net): cap child-controlled sockaddr length before reading it#129

Merged
congwang-mk merged 1 commit into
multikernel:mainfrom
dzerik:fix/net-sockaddr-len-cap
Jul 10, 2026
Merged

fix(net): cap child-controlled sockaddr length before reading it#129
congwang-mk merged 1 commit into
multikernel:mainfrom
dzerik:fix/net-sockaddr-len-cap

Conversation

@dzerik

@dzerik dzerik commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Problem

A sandboxed child can force a multi-GiB allocation in the supervisor by passing an oversized sockaddr length. connect/sendto/sendmsg/sendmmsg take the address length straight from child-controlled args (addr_len = args[5], msg_namelen, both u32 up to 0xFFFFFFFF). The seccomp-notify trap fires at syscall entry, before the kernel's own addrlen > sizeof(sockaddr_storage)EINVAL check, so the length reached read_child_mem uncapped and read_child_mem_vm did vec![0u8; len].

Repro (any active net interception):

int fd = socket(AF_INET, SOCK_DGRAM, 0);
sendto(fd, buf, 1, 0, &valid_sockaddr_in, 0xFFFFFFFF);

The supervisor allocates ~4 GiB for a sockaddr that is at most 128 bytes. Under default overcommit this is a repeatable transient memory-amplification; under RLIMIT_AS / overcommit_memory=2 / low RAM the Rust allocation fails and handle_alloc_error aborts the whole supervisor — a fail-open crash of the security monitor that takes down every sandbox it runs.

Every other child-controlled read is already bounded (MAX_SEND_BUF 64 MiB, MAX_CONTROL_BUF 16 KiB, iovlen 1024, vlen 256). The sockaddr length was the one uncapped read.

Fix

Clamp each of the six sockaddr reads to MAX_SOCKADDR_LEN = size_of::<sockaddr_storage>() (128). The gate only needs the family and address bytes, and the actual send is either re-run by the kernel (Continue) or performed on-behalf with our own correctly-sized sockaddr, so no legitimate call (a real sockaddr is ≤ 128 bytes) is affected.

Test

cargo build clean; full sandlock-core lib suite green (444), including the network tests.

Notes

@congwang-mk

congwang-mk commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Good catch! Instead of silent cap, is returning Errno(EINVAL) better?

@dzerik

dzerik commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Agreed — EINVAL is the better call (it's what the kernel returns for addrlen > sizeof(sockaddr_storage), and it avoids the silently-truncated read). Done in a60730c.

Factored the six sockaddr reads through one read_sockaddr helper that guards the length before the allocation and rejects an oversized one with EINVAL (a read fault still maps to EIO). The four handler-level sites propagate it directly; the two classifier helpers (unix_sendmsg_gate, mmsg_entry_named_unix_path) keep .ok()?, so an oversized length there falls through to a guarded read on the send/prescan path that surfaces EINVAL — no path allocates before the guard.

Updated the regression test accordingly: the 4 GiB addr_len sendto now expects EINVAL (22) rather than the gate result, and still asserts the supervisor survives.

@congwang-mk

Copy link
Copy Markdown
Contributor

@dzerik I may misunderstood your message as I merged PR #128 before this one. Do you mind to rebase? Sorry for the inconvenience. Thanks

…ing it

The seccomp-notify trap fires at syscall entry, before the kernel's own
`addrlen > sizeof(sockaddr_storage)` -> EINVAL check, so a child can pass an
`addr_len`/`msg_namelen` up to u32::MAX. Reading it verbatim into
`vec![0u8; len]` let the child force a multi-GiB supervisor allocation
(OOM / alloc-abort of the monitor). Add a `read_sockaddr` helper that rejects a
length larger than a `sockaddr_storage` with EINVAL (matching the kernel) before
the allocation, and route the six connect/sendto/sendmsg/sendmmsg sockaddr reads
through it; a read fault still maps to EIO. Regression test drives a bogus 4 GiB
addr_len and asserts EINVAL plus a surviving supervisor.

Rebased onto main after multikernel#128 (net parse/decide/execute refactor): the helper
lives in network/mod.rs and the six reads are in the connect/send/unix submodules.
@dzerik dzerik force-pushed the fix/net-sockaddr-len-cap branch from a60730c to 1750fd5 Compare July 10, 2026 13:12
@congwang-mk congwang-mk merged commit b351fe4 into multikernel:main Jul 10, 2026
13 checks passed
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