Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ loopback delivers in. A red there prints the host's name and both answers.
`ubuntu-24.04`, whose log names
`a_host_gives_a_connection_the_nodelay_its_listener_had_when_it_began ... ok`.

The pipe ABI carries both halves (`sdk/toyos/src/net.rs`): a bind's request
carries its listener's options and `MsgType::TcpListenerSetOption` sets one
afterwards, and an accept's answer says what its connection holds
The protocol carries both halves (`sdk/toyos/src/net/socket.rs`): a listen's
request carries its listener's options and its holder's `SET_OPTION` sets
one afterwards, and an accept's answer says what its connection holds
(`TcpOptions`). netstack answers the rule for both by the node's calls
(`system/netstack/src/serve.rs`, `system/netstack/node/src/listeners.rs`),
and the node takes a listener's option in its `listen` and in
Expand All @@ -33,7 +33,7 @@ connection took from its listener. The `libc_sockets` guest test that read it
`setsockopt` and `getsockopt`) was cut in `ff1a11dbd`. What no program reads:

- Whether a C program can set the option while a connection waits and know
that one does. libc's `poll` now watches a listener by its wake pipe
that one does. libc's `poll` now watches a listener by its holder's bell
(`sdk/libc/src/posix_io.rs`); no test reads that it answers a waiting
connection.
- No Rust program can set the option on a listener. `std::net::TcpListener`
Expand All @@ -47,17 +47,15 @@ connection took from its listener. The `libc_sockets` guest test that read it
(`836d14f`) delegates a stream's option to std's. std's
`TcpListener::accept` (`sdk/std/sys/net/connection.rs`) takes the accepted
stream's option from the answer, which no test can turn red while no std
listener can hold one; `toyos::net` still has two bind calls, `tcp_bind`
and `tcp_bind_with`.
listener can hold one.

**Exit condition**: on ToyOS a program that sets `TCP_NODELAY` on a
listening socket and accepts one connection begun before and one begun after
reads the option on each as that test has a host answer, by libc's
`getsockopt`, with libc's `poll` answering for the waiting listener, and by
std's `nodelay()`, which needs a listener a Rust program can set the option
on: the socket2 fork's, with the lock naming the commit that hands its
listener's option to netstack in its bind; and `toyos::net` has one bind
call, which takes its options.
listener's option to netstack in its listen.

**Owner**: whoever holds `issues/toyos-has-its-own-network-stack.md`; the
fork commit is on socket2's `toyos` branch.
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ kind: defect
opened: 2026-10-12
---

# Only a connected stream is shared with a child

A TCP stream is shared by its holders (`sdk/toyos/src/net/stream.rs`): the
process that connected or accepted it, and every process a spawn gave a
descriptor of it, which exports a holder (`fd:<n>:stream`,
`sdk/toyos/src/fd.rs`) that the child takes up. Each holder keeps working when
another exits or is killed. A stream's own handle stays in its process:
`endow` and `provide` of one are refused with `Misuse::Moved`.

Every other socket is not shared:
- Listeners and datagram sockets are named by ambient ids
(`issues/netstack-socket-ids-are-ambient.md`). A spawn passes a std
listener's descriptor as its wake pipe's bare handle, which names nothing the
child can use.
- A C socket that has not connected is libc's entry alone, an object of
libc's own, which a spawn refuses (`NotSupported`).
# A socket libc or socket2 has not bound is not shared with a child

A TCP stream, a listener and a datagram socket are shared by their holders
(`sdk/toyos/src/net/stream.rs`, `sdk/toyos/src/net/socket.rs`): the process
that connected, accepted, listened or bound, and every process a spawn gave a
descriptor of it, which exports a holder (`fd:<n>:exported`,
`sdk/toyos/src/fd.rs`) that the child takes up. A stream's holder keeps
working when another exits or is killed. A stream's own handle stays in its
process: `endow` and `provide` of one are refused with `Misuse::Moved`.

`netstack_holders_exported_to_children` (`tests/toyos.rs`) reads a listener's
and a datagram socket's holders in a parent and its child, one after the other
and both waiting on one queue; no guest test reads a survivor after the other holder
is killed.

What is not shared:
- A C socket that has neither bound nor connected is libc's entry alone, an
object of libc's own, which a spawn refuses (`NotSupported`).
- A socket2 socket's descriptor is an object of socket2's own, so a spawn
refuses it (`NotSupported`), and `Socket::from_raw_fd` of a stream's
descriptor answers every operation "invalid socket fd".
Expand All @@ -44,10 +46,9 @@ so listeners and datagram sockets are shared as streams are.
Planning's reading of the evidence (2026-10-21): data a dead holder already
consumed need not be recovered.

Order: listeners and datagram sockets as holder sessions, after the pipe page;
then C sockets shared before they connect; the spawn from the inheritable flag
last, since every interim rule for an inheritable socket is rejected or
unapproved.
Order: C sockets shared before they bind or connect; the spawn from the
inheritable flag last, since every interim rule for an inheritable socket is
rejected or unapproved.

Owner: the orchestrator, who stages
`issues/netstack-moves-every-byte-through-two-kernel-pipes.md`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ fa7a35ac0091a05b — the holder left no readable note`, 930 s by 14:48:17, with
`lsof` showing the key's lock file open twice in the harness and nowhere else.
`--ci host` again ran beside it and had finished at 14:45:47.

It recurred on #1052's round 2, with nothing beside it: right after a `cargo
clean`, `cargo test --test toyos-build -- netstack_holders_exported_to_children
stream_holders libc_children iommu_virtio_platform` (harness pid 73228) took
the build lock exclusive to build `3c919ba2399d86e1` at 20:29:49 and finished
it at 20:31:15. Three of its tests ran and passed; `stream_holders`'s worker,
whose `stream_handles` build had begun at 20:29:45, waited on `using sysroot
3c919ba2399d86e1` from 20:29:49, first "held by pid 73228 (building sysroot
3c919ba2399d86e1)" and after the build "the holder left no readable note", for
1,290 s by 20:51:19, when the run was stopped. Alone, the same row passed in 8 s
(PR #1052's round 2 evidence, `guest-r2.log` and `guest-r2b.log`).

**Exit condition**: no path holds the worktree build lock while it waits on a
key's lock, and a suite started on a head whose sysroot is not yet made
finishes.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ opened: 2026-09-08
`sdk/libc/src/socket.rs`'s `getsockname()` fills the caller's `sockaddr`
with `[10, 0, 2, 15]` and the socket's local port. The address is a literal in
that function; netd is not asked, and the SDK has nothing to ask it with —
`toyos::net` carries `tcp_connect`, `tcp_bind`, `tcp_accept`, the UDP calls and
`dns_lookup`, and no call that answers "what is this machine's address".
`toyos::net` carries a stream's connect, a listen, an accept, the datagram
calls and `dns_lookup`, and no call that answers "what is this machine's
address": a socket's `toyos::net::socket::local` is the address its bind
named.

It was true by coincidence until now: netd carried the same literal, so the
shim and the stack agreed. netd takes its address from DHCP as of the change
Expand Down
23 changes: 0 additions & 23 deletions issues/netstack-holds-a-pending-request-for-a-client-that-left.md

This file was deleted.

12 changes: 7 additions & 5 deletions issues/netstack-moves-every-byte-through-two-kernel-pipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The design and its roast are PR #863's body.
on, which then carries only doorbells and the hang-up; std, libc, mio and
socket2 reach it through `toyos::net::stream`, and `SYS_CONNECTION_JOIN` is
gone. A stream is shared with a child as a holder of its own; other
sockets are not yet (`issues/only-a-connected-stream-is-shared-with-a-child.md`). A sent byte is copied
sockets are not yet (`issues/a-socket-libc-or-socket2-has-not-bound-is-not-shared-with-a-child.md`). A sent byte is copied
twice by netstack, out of the lent send ring into a scratch buffer and from
it into the frame, since the frame builder takes one slice; a builder that
copies each span of the ring into the frame itself would remove the first.
Expand All @@ -69,13 +69,15 @@ The design and its roast are PR #863's body.
0.00015) against 2.0029, the pipe lock held 0.179 ms against 0.587 and spun
0.092 against 0.108, C0 7.735 ms against 7.770, 812.7 Mb/s against 824.1
(https://github.com/ToyOSOrg/evidence/tree/7cc1bda0dcb038084de60d65c64f30a3956a13a3/round3).
- Stage 4: a listener's connection carries its wakes in place of its pipe, a
datagram socket is a region as a stream is, and the pipe ABI's messages and
`toyos::net`'s pipe handles go. netstack copies each datagram record's
- Stage 4: a datagram socket is a region as a stream is. A listener and a
datagram socket are already their holders' connections and bells
(`toyos::net::socket`), each datagram one frame on its holder's
connection, and the pipe ABI's messages and `toyos::net`'s pipe handles are
gone; a datagram is still copied through the connection's pipes. netstack copies each datagram record's
`len`, `port` and `addr` out of the region once and judges the copy: a
`len` past the bytes the record holds ends that socket alone. `toyos::net`'s `send_to` refuses
what a sender can judge and writes no record: a payload past
`toyos_abi::net::UDP_MAX_PAYLOAD`, as POSIX's `sendto` fails
`toyos::net::socket::MAX_DATAGRAM`, as POSIX's `sendto` fails
`[EMSGSIZE]`; port 0; an unspecified address. That bound is the one
declaration: [udp]'s `limits::MAX_PAYLOAD` goes, and [udp], the node's
`name.rs` and `resolve.rs`, netstack's `serve.rs` and [udp]'s
Expand Down
37 changes: 0 additions & 37 deletions issues/netstack-socket-ids-are-ambient.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ToyOS, socket2's `setsockopt` (`src/sys/toyos.rs`) matches
`(SOL_SOCKET, SO_BROADCAST)` to an empty arm and answers `Ok`, and its
`getsockopt` answers 0 for it: a setter that says yes to a permission it hands
to nobody, where std's `UdpSocket::set_broadcast` and libc's `setsockopt` send
`toyos::net::udp_set_option`. No send follows it today: the backend holds no
`toyos::net::socket::set_option`. No send follows it today: the backend holds no
datagram socket at all, so a program that reaches a datagram socket through
socket2 has none on ToyOS.

Expand Down
2 changes: 1 addition & 1 deletion issues/toyos-builds-itself.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ An owner is an issue under `issues/`, or this track.
| 18 | libc gains `select`, `setvbuf` and `socketpair`. A descriptor is a small table number since #978, so `FD_SETSIZE` holds | not started | this track | — |
| 19 | The `libc` crate gains a ToyOS module, checked by `ctest` against `sdk/libc`'s headers | not started | this track | 18 |
| 20 | Children: `SIGCHLD`, sessions, a quit notice and Ctrl+C, the process track's stages 4–7 | in progress | `a-childs-end-is-an-event-and-a-parent-takes-its-children-down` | — |
| 21 | Sockets shared by their holders and inherited by a child, which curl, libssh2 and every inheriting server need | direction set | `only-a-connected-stream-is-shared-with-a-child`, `libc-close-of-a-socket-is-unproven-on-a-connected-socket` | engineering: streams shared; listeners, datagram sockets and inheritance next |
| 21 | Sockets shared by their holders and inherited by a child, which curl, libssh2 and every inheriting server need | direction set | `a-socket-libc-or-socket2-has-not-bound-is-not-shared-with-a-child`, `libc-close-of-a-socket-is-unproven-on-a-connected-socket` | engineering: streams, listeners and datagram sockets shared; unbound C sockets and inheritance next |
| 22 | cargo's eight C libraries cross-built: curl, libgit2, libssh2, OpenSSL, SQLite, nghttp2, zlib, blake3; five stopped on headers, types and functions libc lacked at `15625e0cb` | not started | this track | 18, 19 |
| 23 | cargo's TLS: OpenSSL through its Perl `Configure`, or curl's rustls backend on `ToyOSOrg/ring`. A build from vendored sources needs neither, and neither needs the `toyos-crypto` track | not started, choice open | this track, `the-internet-clients-work-unchanged` | 22; HTTPS on `the-lan-is-not-yet-production-grade` |
| 24 | cargo at run time: its SQLite and its locks, sockets and children | not started | this track, `sqlite-and-postgresql-run-on-toyos` | 14–17, 20–22 |
Expand Down
Loading