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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ opened: 2026-09-28
# A client waits on the compositor's answer with no bound

`window::clipboard_set` (`sdk/window/src/lib.rs`) blocks in
`recv_header` for `MSG_COPY_REGION` on a copy past `MAX_INLINE_PAYLOAD`, and
`FrameRx::recv` for `MSG_COPY_REGION` on a copy past `MAX_INLINE_PAYLOAD`, and
`Window::create` blocks the same way for `MSG_WINDOW_CREATED`. Neither wait has
a deadline, so a compositor that is alive and not answering wedges every
terminal or editor that copies, and every program that asks for a window.
Expand Down
33 changes: 33 additions & 0 deletions issues/a-forced-reboots-last-userland-lines-miss-the-console.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
status: open
kind: defect
opened: 2026-11-01
---

# A forced reboot's last userland lines can miss the serial console

`session_lifecycle` (`tests/common/sessionlifecycle.rs`) forces a waiting
reboot and then waits on the console for two userland lines written just
before it: the job's `session_lifecycle: ok, and the reboot is forced`, said
the statement before its terminate, and the supervisor's `power: the stop ends
what stays`. In some runs one of them never reaches the wire. The kernel's own
records still do: the forced stop's kills of the holder and its session, and
`Rebooting.`. So the stop did happen as the job asked, and only the lines are
lost. The harness then reports `STALLED: waiting for the job to reach the
forced reboot`, or `the supervisor never said what the forced reboot ended`.

**Measured** on the remote QEMU x86-64 runner, `cargo test --test
toyos-build -- session_lifecycle`, 2026-11-01:
- #1108 `18060b521` (byte-stream connections): 1 red of 4.
- #1121 `e396bb5f1` (whole-message connections): 3 red of 6 so far.

Faster connections shorten the time from the terminate to the stop, so the
window opens more often, but it is there without them.

Where the lines go between a program's log ring and the wire at a stop
(`logkeeper`'s flush answers the supervisor before the stop goes on;
`console: klogd let the wire go to the stop`) is not yet traced.

**Exit**: every userland line written before a forced stop's request reaches
the console before the machine resets, and `session_lifecycle` is green 20
runs in a row on the runner.
31 changes: 20 additions & 11 deletions issues/a-pipes-lock-spans-a-user-copy-and-its-first-allocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,28 @@ opened: 2026-08-28

Each pipe owns its state behind its own `sync::Lock` (`kernel/src/pipe.rs`),
which disables preemption before it spins (`kernel/src/sync.rs`). The bulk copy
runs inside that lock:
runs inside that lock, through the frame store pipes and connections share
(`kernel/src/frame_bytes.rs`):

* `PipeRef::try_read_within` makes one walk of the caller's window,
* `FrameBytes::read` makes one walk of the caller's window,
`buf.write_pieces`, and copies each pinned piece out of the frames it
covers with `bytes.peek`, under it;
* `PipeRef::write` makes one walk, `buf.read_pieces`, and copies each piece
* `FrameBytes::write` makes one walk, `buf.read_pieces`, and copies each piece
into its frames with `bytes.place`, in the same acquisition.

The copy is bounded only by the pipe: `PIPE_CAPACITY` is 2 MiB, the largest
single copy under the lock, and nothing above caps it (`SYS_READ`/`SYS_WRITE`
pass the caller's length through). Each 16 pages of it take the caller's space
lock and populate any untouched page, a frame from the allocator and a 4 KiB
zeroing.
Each 16 pages of either copy take the caller's space lock and populate any
untouched page, a frame from the allocator and a 4 KiB zeroing.

A pipe's copy is bounded only by the pipe: `PIPE_CAPACITY` is 2 MiB, the
largest single copy under the lock, and nothing above caps it
(`SYS_READ`/`SYS_WRITE` pass the caller's length through).

A connection direction (`kernel/src/message.rs`) copies one whole message under
its queue's lock, so its copy is bounded at `MAX_MESSAGE_LEN`, 16 KiB, and its
frames are taken the same way. Both its send and its receive run that copy
inside `process::with_process_data`, nesting the queue's lock in the process
lock: the receive installs the message's handles in the caller's table and
copies its bytes in one hold, so a refusal takes nothing.

A write that grows the pipe also takes its frames under the same lock,
nesting the frame allocator's lock: up to 513 frames, unzeroed, under one
Expand All @@ -42,8 +51,8 @@ bounded per acquisition, has to carry the data-race argument. The frames can
be taken before the lock and given back after it, as the read end's last
holder already gives back what the pipe held.

**Exit**: no pipe lock is held across more than a bounded copy or any
allocation, the hold time is read before and after, and `audio_tone`'s worst
wake does not move.
**Exit**: no pipe or queue lock is held across more than a bounded copy or any
allocation, the connection's nesting in the process lock included, the hold
time is read before and after, and `audio_tone`'s worst wake does not move.

This is stage S0-a of `issues/anonymous-memory-swaps-to-an-encrypted-partition.md`.
18 changes: 9 additions & 9 deletions issues/a-received-handle-has-no-knowable-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ opened: 2026-08-10

# A process cannot ask what most handles it received are, and using one wrongly is fatal

`SYS_HANDLE_RECV` installs a peer's handles in the receiver's table and answers
only *how many*. `SYS_FSTAT` answers what any handle is, needing no right, and
`SYS_MESSAGE_RECV` installs a message's handles in the receiver's table and
answers only *how many*. `SYS_FSTAT` answers what any handle is, needing no right, and
settles two kinds: only a region of memory the kernel allocated answers
`SharedMemory`, which is how `SharedMemory::lent` refuses every other handle
before it maps one (`sdk/toyos/src/shm.rs`), and only a `Connection` answers
Expand Down Expand Up @@ -47,11 +47,10 @@ in its place sees it refused while the receiver lives.

## Three ways out, and none is free

1. **Report the kind.** `SYS_HANDLE_RECV` writes `(RawHandle, kind)` pairs
instead of bare handles. One ABI shape change, in-tree only — no fork names
`handle_recv` (the whole fork estate was swept for this branch and
`handle_recv` is not in it). The receiver then refuses by
name and the fail-fast policy is untouched.
1. **Report the kind.** `SYS_MESSAGE_RECV` writes `(RawHandle, kind)` pairs
instead of bare handles. One ABI shape change, in-tree only: no fork calls
`message_recv` or the SDK's receive. The receiver then refuses by name and
the fail-fast policy is untouched.
2. **A syscall** answering the kind of a handle. Cheaper to write and
worse: it is a second place to ask, and it makes "what is this" a round trip
rather than part of the answer that produced it.
Expand All @@ -67,8 +66,9 @@ Judged while clearing PR #22's blockers, with the reasoning written down because
the next reader will ask why a class this wide was left open.

- **The fix is an ABI shape change and the ABI was the owner's to approve.** It
widens `SYS_HANDLE_RECV`'s answer from `n` to `n` pairs, which is a syscall
the owner approved changing shape after the fact.
widens the receive's answer from `n` to `n` pairs (`SYS_HANDLE_RECV` then,
`SYS_MESSAGE_RECV` since), which is a syscall the owner approved changing
shape after the fact.

So it stays open, and what it is waiting on is a decision rather than an
instrument.
Expand Down
4 changes: 3 additions & 1 deletion issues/a-tcp-connect-zeroes-twelve-mebibytes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ may never use:
`SYS_SHM_CREATE` takes and zeroes each of its 2048 frames before it answers
(`kernel/src/object/shm.rs`), and netstack maps all of them before the SYN
leaves.
- **The connection's two pipes**, 2 MiB each: the request's page on the
- **The connection's two directions**, 2 MiB each: the request's page on the
client's write and the answer's on netstack's, each zeroed whole on that
first write (`issues/a-pipe-is-a-two-mebibyte-page-for-a-mapping-nothing-makes.md`).
Since #1074 a direction takes 4 KiB frames as it fills and zeroes none, so
this part is measured no longer.

**Measured** by the paired `osbench lan` sessions from the T14's I219 to the
AMD bench's Linux peer over its Wi-Fi:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ The design and its roast are PR #863's body.
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
gone; a datagram is still copied through the connection. 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
Expand Down
2 changes: 1 addition & 1 deletion issues/the-capability-end-state-is-twelve-answers.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ process has.
is created with `Rights::NONE` there because "every bit on a `SysCap` is an
authority init decides per program" — the machine's only one is minted with
`TRANSFER` at `kernel/src/loader/mod.rs:946`. The mechanisms are
`SYS_MESSAGE_SEND`/`SYS_HANDLE_RECV` over a `Connection`, where the connection
`SYS_MESSAGE_SEND`/`SYS_MESSAGE_RECV` over a `Connection`, where the connection
and every handle must carry `TRANSFER`, no handle may be named twice, and the
connection may not be sent over itself (`4a98107f^:kernel/src/arch/syscall.rs:2086`,
`:2090`); and `SpawnArgs::endow`. Nothing else crosses a process boundary — the
Expand Down
Loading