Skip to content

Commit 108232f

Browse files
Japabuclaude
andcommitted
Merge main, which carries libc's sockets (#787), into the node stage
One conflict, in the track's line on the broadcast permission. #787 changed that line's last sentence only: the C case now runs the sequence without `bind` too, held by `tests/netcase/sendto_unbound.c`, where the sentence waited on an issue #787 closes and deletes. This branch rewrote the rest of the line. The line is this branch's with #787's ending. The line after it, on a logged refusal waiting in the stack, stays deleted: #787 did not touch it, and this branch met its exit. #787's other hunk in the track, the listener's `TCP_NODELAY` issue named among the listener defects, merged by itself. No line of the merged tree names any of the four issue files #787 deleted, by path or by bare name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvnWQFcMuGqTHYhvSnTe8A
2 parents e8a806c + 9cf2c4e commit 108232f

27 files changed

Lines changed: 1308 additions & 364 deletions

‎issues/a-counters-read-under-host-load-can-go-silent-for-15-s.md‎

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ opened: 2026-10-04
88

99
`test_rs_counters_read` in `tests/virtsmpcase` normally ends about 0.2 s of
1010
guest clock after `unmap_touch` does (median 0.17-0.20 s over 570 guests, max
11-
1.46 s). Under host load it sometimes takes seconds, and twice it went silent
12-
long enough for the harness's 15 s quiet bound to call the guest stalled.
11+
1.46 s). Under host load it sometimes takes seconds, and it has gone silent
12+
long enough for the harness's 15 s quiet bound to call the guest stalled: each
13+
silent boot is below.
14+
15+
`virt_mask_windows` boots the same case on a `mask-windows` kernel and waits
16+
on two things, the job `unmap_touch` and then the boot's last word, so a
17+
silence in the counters read reds it as `STALLED: waiting for the boot's last
18+
word`: that wait spans the read, `test_rs_trace_read` and the shutdown. Its
19+
stalls are recorded here.
1320

1421
The first silent one: `virt_el1_smp`, 8 vCPUs under TCG, on a 14-core host at
1522
1-minute load 38-53 while the primary checkout was building the stage-2
@@ -54,9 +61,86 @@ line of the wait and none the guest runs.
5461
- **What is not known.** Whether the guest was running, spinning or not being
5562
scheduled by the host, and whether it was the guest or its console that
5663
stopped. No register capture of it exists either, so it adds a count and a
57-
second profile and brings the exit no closer. The fork compiler miscompiles,
58-
for AArch64, an inclusive range that ends at its integer type's maximum;
59-
whether that reaches this job's code has not been examined.
64+
second profile and brings the exit no closer.
65+
66+
The third and fourth silent ones, two guests of one run: `virt_el1_smp` and
67+
`virt_mask_windows`, in the whole suite on `wt/toyos-libc-sockets` at
68+
`68cf1f870`, whose diff touches no line of either wait and nothing these
69+
guests start: `tests/virtsmpcase` runs no C program, opens no socket and
70+
starts no netstack.
71+
72+
- **The lines.** `FAIL virt_el1_smp: STALLED: waiting for the job
73+
test_rs_counters_read to end — it went quiet` and `FAIL virt_mask_windows:
74+
STALLED: waiting for the boot's last word — it went quiet`, both at the same
75+
second of the host's clock; 35 of 37 passed.
76+
- **The load.** `--jobs 4 --nocapture` on the 14-core host, 1-minute load
77+
75.99 when the run began and 63.37 when it ended, from other worktrees'
78+
builds; liveness ceilings paid at 1.52x; workers 366 s building against
79+
808 s testing.
80+
- **The waits.** `virt_el1_smp`: `judge_virt_job`'s `await_marker` on
81+
`===TEST_END test_rs_counters_read `. `virt_mask_windows`: its second wait,
82+
`await_marker` on `power::SHUTTING_DOWN`, after `judge_virt_job` had judged
83+
`unmap_touch`. Each was ended by `GUEST_QUIET`.
84+
- **The same profile as the second.** Each guest's last three lines are the
85+
end of `unmap_touch`, the start of the read and the kernel's record of its
86+
spawn, with no thread's exit after it. `virt_el1_smp`: `===TEST_END
87+
unmap_touch exit=0===` at 13.577, `===TEST_START test_rs_counters_read===`
88+
at 13.578, `spawn: /system/bin/test_rs_counters_read pid=13` at 13.590 on
89+
cpu4. `virt_mask_windows`: the same three at 14.557, 14.558 and 14.585, on
90+
cpu4. The two spawn records reached the harness within one second of each
91+
other, and nothing followed from either for the 15 s.
92+
- **What is known.** `virt_smp`, the same case launched one second before
93+
`virt_el1_smp`, did the step in the same seconds and passed: the read began
94+
at guest 13.936 and ended at 19.120, five seconds of the host's clock.
95+
`virt_failed_ap_leaves_no_hole`, launched one second after, ends at
96+
`unmap_touch` and does not wait on the read. Each red, run alone at the
97+
same commit, passed in 4 s, at loads 32.47 and 37.04.
98+
- **What is not known.** Whether the guests ran, spun or were not scheduled:
99+
no register capture exists. Two guests going silent at the same step in the
100+
same second is the first occurrence that bears on the host stopping the
101+
guest, and it shows no more than that. The serial directory the harness
102+
named for that run was not kept; the run's log, outside the tree, is the
103+
only copy.
104+
105+
Two other stalls of `virt_mask_windows` have no console of the guest, so
106+
the step each stopped in is not read from it:
107+
108+
- **`wt/toyos-move-abi` at `94fd25e71`.** `FAIL virt_mask_windows: STALLED:
109+
waiting for the boot's last word — it went quiet`, ten minutes after the
110+
two at `68cf1f870` by the two runs' logs, with nothing in what the
111+
guest said while it was waited on, `STALL virt_mask_windows (38s)`; the
112+
whole suite at `--jobs 4`, 34 of 36, workers 1794 s building against 703 s
113+
testing. Load 59.30 69.33 70.48 when the suite began and 85.73 69.02 67.23
114+
when it ended, 14 cores; fastest boot 2765 ms against the reference
115+
1424 ms, ceilings at 1.94x. The wait is the second one, ended by
116+
`GUEST_QUIET`, which no host factor stretches. Known: `unmap_touch` had
117+
ended with exit 0, its line reaching the harness 15 s before the verdict,
118+
the guest named by elimination among the three of that case then running.
119+
In the same run `virt_smp` said its `unmap_touch` line at guest 21.786 and
120+
its `counters_read` line at 34.278, 13 s apart on the host's clock and 2 s
121+
inside the bound; `virt_el1_smp`, earlier in that run, 13.481 and 15.702.
122+
The branch changes nothing that guest's kernel or harness is built from.
123+
At `c95d0ffc0` the test passed in the whole suite at load 12.82 rising to
124+
36.76 and alone at 36.76 to 42.53, neither at the red's load. Not known:
125+
whether the guest was alive or what it did in those 15 s; the red run's
126+
kept serial directory holds eleven UART logs, each an x86-64 boot, and no
127+
PL011's. Whether the silence is `virt_smp`'s 13 s grown past 15 under the
128+
hooks' cost, or a guest that stopped. It was not run again at that commit,
129+
nor on `main` at that load.
130+
- **`wt/toyos-acpi1` at `b9430ed61`.** The same line after 45 s, in the whole
131+
suite; workers 19038 s building against 1375 s testing, load 82.65 83.62
132+
77.89 moments after it, 14 cores. Nothing in that branch runs on that
133+
boot. Alone at that load and commit it passed in 14 s. On `main` at
134+
`d47b383cf`, `cargo test -- virt_mask_windows` passed three times: alone at
135+
load 45 rising to 72 (10 s), and twice beside a whole suite at load 74 to
136+
77 (38 s, with ceilings paid at 8.00x and 31 s between its image and its
137+
first guest line) and 75 to 67 (7 s).
138+
139+
Not known of any of them: whether the fork compiler's fault reaches
140+
`test_rs_counters_read` or the kernel under it.
141+
`issues/the-forks-llvm-deletes-a-loops-exit-on-a-no-wrap-flag-scalar-evolution-gives-the-wrong-value.md`
142+
has LLVM's ScalarEvolution deleting a loop's exit on both architectures, and
143+
records every function but the ones it names as not measured.
60144

61145
The slow ones, with registers: a probe that captured `info registers -a` over
62146
QMP whenever the read had not ended 3 s after `unmap_touch` (`debug-slow.patch`
@@ -84,3 +168,6 @@ whether its CPUs went idle by a `sched:` line's absence; its diary
84168
Exit: the cause of the silence is named from a capture of a silent guest
85169
(registers over QMP before anything else touches it), and fixed with the
86170
evidence, or shown to be the host stopping the guest.
171+
172+
Owner: the kernel's AArch64 bring-up, `issues/toyos-runs-on-arm64.md`, held
173+
by the orchestrator's next kernel worker.

‎issues/a-listening-sockets-tcp-nodelay-is-refused-and-an-accepted-streams-is-answered-from-a-guess.md‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,26 @@ connection waits to be accepted: a connect returns before the listener's end
1717
is established, so the answer rests on that event and not on the order
1818
loopback delivers in. A red there prints the host's name and both answers.
1919

20-
ToyOS as it ships does neither half:
20+
ToyOS as it ships does neither half for an option set on a socket that
21+
listens:
2122

22-
- netstack answers `TcpSetOption` and `TcpGetOption` `ERR_NOT_CONNECTED` for
23+
- netstack answers `TcpSetOption` `ERR_NOT_CONNECTED` for
2324
any id that is not a stream (`handle_tcp_set_option`,
2425
`userland/netstack/src/main.rs`), and libc's `setsockopt` sends a listening
2526
socket's id there (`userland/libc/src/socket.rs`), so a C program that sets
26-
the option on its listener is refused.
27+
the option on its listener is refused, -1 and `ENOTCONN`, and the listener
28+
keeps what it held. libc holds a listener from `bind`. One set before that
29+
it keeps, and `accept` hands it to netstack for each connection, as
30+
`connect` does: with every later set refused it is the option the listener
31+
held when any connection began. `tests/netcase/nodelay_kept.c` holds the
32+
refusal, and the accepted socket's read for a listener that held the option
33+
at `bind` and for one that did not; its three lines on the refused set are
34+
the ones this file's exit turns to the host's answers.
2735
- std builds every `TcpStream`, an accepted one included, with
2836
`nodelay: false` and answers `nodelay()` from that field without asking
2937
netstack (`sdk/std/sys/net/connection.rs`). Nothing can make that false
30-
today, since no accepted connection has the option. Once a listener's
38+
today: std has no setter for a listener, and what libc's `accept` hands
39+
over is for a socket of libc's. Once a listener's
3140
option reaches the connections it accepts, it is false for each of them.
3241

3342
The node (`userland/netstack/node/src/listeners.rs`) has the calls,

‎issues/a-stream-its-peer-reset-refuses-the-option-requests-a-host-answers.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,17 @@ entry (`userland/libc/src/socket.rs`). Keeping the id until the client's close
2929
request is not the fix: a client that dies sends none, and netstack, having
3030
closed its pipe ends, has nothing left that says the client is gone.
3131

32+
libc's `connect` asks the same request of a connection it has just been
33+
answered, to hand over a `TCP_NODELAY` set before it, and its `accept` of one
34+
it has just been given, to hand over its listener's: a peer that resets
35+
between netstack's answer and that request makes either fail `ENOTCONN`, with
36+
the connection closed, which no host's `connect` or `accept` answers.
37+
3238
**Exit condition**: a guest test on `tests/netcase` whose peer resets a stream
3339
the client still holds, and `nodelay()` on it answers `Ok`, with
34-
`netstack_socket_churn` still green.
40+
`netstack_socket_churn` still green; and the two compromises above ended: the
41+
option request on a stream its peer reset is answered by netstack, and a libc
42+
`connect` and a libc `accept` whose peer resets before the hand-over answer as
43+
the host does, each held by a test that can be red.
3544

3645
**Owner**: whoever holds `issues/toyos-has-its-own-network-stack.md`.

‎issues/libc-drops-a-tcp-nodelay-set-before-connect.md‎

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
status: open
3+
kind: defect
4+
opened: 2026-10-08
5+
---
6+
7+
# libc's getaddrinfo ignores its service and its hints
8+
9+
Read from the code, not run. `getaddrinfo` (`userland/libc/src/socket.rs`)
10+
names its second and third arguments `_service` and `_hints` and reads
11+
neither. Every answer's port is 0, so the usual C client,
12+
`getaddrinfo(host, "80", &hints, &res)` and `connect(fd, res->ai_addr,
13+
res->ai_addrlen)`, dials port 0. Every answer's `ai_socktype` is
14+
`SOCK_STREAM` and its `ai_protocol` 0 whatever the hints ask, `AI_PASSIVE`
15+
with a null node answers failure where POSIX has the wildcard address, and
16+
`AI_NUMERICHOST` still asks the resolver. Every failure answers -1, which is
17+
none of the `EAI_` codes `include/netdb.h` defines, and `gai_strerror` has
18+
one text. A numeric host is read as `inet_pton` reads one, four decimal
19+
numbers, where POSIX has `inet_addr`'s forms.
20+
21+
**Exit**: `getaddrinfo` answers the service's port, numeric or refused by
22+
name, and the hints' socket type, protocol and flags, each failure is the
23+
`EAI_` code POSIX names for it, and a guest C case connects to an address
24+
and port `getaddrinfo` answered.
25+
26+
**Owner**: libc, under `issues/toyos-has-its-own-network-stack.md`: it
27+
touches only `userland/libc` and a guest C case on `tests/netcase`.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
status: open
3+
kind: defect
4+
opened: 2026-10-09
5+
---
6+
7+
# libc's getsockname and getpeername answer a null buffer with success
8+
9+
`getsockname` and `getpeername` (`userland/libc/src/socket.rs`) answer through
10+
`fill_sockaddr`, which returns having written nothing where the address
11+
buffer or the length pointer is null, and each call then answers 0. A program
12+
that passed a null pointer is told its address was written.
13+
14+
A host refuses it. Measured on Darwin 27.0.0, one C program, each call on a
15+
connected stream:
16+
17+
| | `getsockname` | `getpeername` | ToyOS, read from the code |
18+
|---|---|---|---|
19+
| null buffer, length 16 | -1 `EFAULT` | -1 `EFAULT` | 0, the length left at 16 |
20+
| null length | -1 `EFAULT` | -1 `EFAULT` | 0 |
21+
| null buffer, length 0 | 0, the length written 16 | 0, the length written 16 | 0, the length left at 0 |
22+
23+
Linux is unread; by the review that found this, it reads the buffer only when
24+
the length is not 0, which is Darwin's third row.
25+
26+
`SockaddrIn::answer` (`userland/libc/src/inaddr.rs`), which the host test
27+
`an_address_is_truncated_to_the_callers_buffer_as_the_host_truncates_it`
28+
(`tests/libc-arch/src/internet_addresses.rs`) holds against the host's
29+
`getsockname` at every length, excludes both pointers by its contract, so no
30+
test reaches the difference.
31+
32+
`accept` and `recvfrom` take a null buffer as a caller that wants no address,
33+
which POSIX gives them, and Darwin's `accept` answers a null pair with a
34+
socket; what either answers a buffer with a null length is not read.
35+
36+
**Exit**: the null cases are in the module a host builds, and a test in
37+
`tests/libc-arch` compares the answer, `errno` and the length written back
38+
with the host's own `getsockname` for the table's three rows, on each host
39+
the `host` check runs on; `getpeername` answers through the same function.
40+
41+
**Owner**: libc, `userland/libc`; whoever next changes its address calls.

‎issues/libc-reads-an-ipv4-address-in-host-order.md‎

Lines changed: 0 additions & 26 deletions
This file was deleted.

‎issues/libc-sends-from-a-datagram-socket-it-never-bound.md‎

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
status: open
3+
kind: defect
4+
opened: 2026-10-08
5+
---
6+
7+
# Nothing reads whether a stream's TCP_NODELAY reached netstack
8+
9+
netstack answers `MsgType::TcpSetOption` done or with an error, and nothing
10+
answers what a stream holds: the pipe ABI has no option read
11+
(`toyos/src/net.rs`), and std's `nodelay()` and libc's `getsockopt` answer the
12+
value their own side stored. So a setter that stores the value and sends
13+
nothing is seen by no test. Measured on libc's `connect`, which hands over a
14+
`TCP_NODELAY` set before the connection existed: with the hand-over deleted,
15+
the guest case that sets the option before `connect` and reads it after
16+
(`tests/netcase/nodelay_kept.c`, the `libc_sockets` test) stays green. And on
17+
its `accept`, which hands over the one its listener held at `bind`: with that
18+
request deleted and the value still stored, the same case's read of the
19+
accepted socket stays green. The
20+
only effect of the option is when a small write leaves the machine, which a
21+
QEMU test may not time.
22+
23+
**Exit**: a test reads what the stack holds for a stream's Nagle switch, on
24+
the node under a host test of its option call or through a guest's wire, and
25+
turns red when libc's `connect` or `accept` or std's `set_nodelay` stores the
26+
value and sends no request.
27+
28+
**Owner**: whoever holds `issues/toyos-has-its-own-network-stack.md`, with
29+
the reset-stream option test of
30+
`issues/a-stream-its-peer-reset-refuses-the-option-requests-a-host-answers.md`,
31+
which asks the same request.

0 commit comments

Comments
 (0)