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
43 changes: 43 additions & 0 deletions issues/a-wait-was-registered-while-its-task-still-read-waiting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
status: open
kind: defect
opened: 2026-08-15
---

# A wait was registered while its task still read *waiting*

The kernel panicked three times on the assertion that a task waits on at most
one queue, now `Watch::register`'s `assert!(task.set_waiting(), "a task waits
on at most one watch")` (`kernel/pure/sched/watch.rs`). What the assertion
reports is a `waiting` flag (`TaskShared::set_waiting`,
`kernel/pure/sched/task.rs`) left set by an earlier wait of the same thread.
Which wait left it set was never established.

## The sightings, all on paths since removed

- Once in logd's `io_uring_enter` on a KVM shard (nightly dispatch
`31900050723`, job `95049280131`, 2026-08-15), 4.7 s after the boot stick
was pulled and the kernel's usb-storage gave the disk up. The same tree was
green on the same name minutes earlier; one guest per machine, so host
contention does not explain it.
- Twice from a host-side keyboard flood of a few thousand key events a second
into the in-guest runner blocked on stdin at `===READY===`, through
`scheduler::wait_until::<kernel::keyboard::has_data>`, once with the i8042
present and once with `q35,i8042=off`. Not at ordinary typing rates.

`io_uring` and the shared wait queues went with the one-watch rewrite
(`aaddf38a7`, `232f69bb6`), and the kernel's keyboard with the i8042 and USB
drivers leaving it (`febfd21e8`, `13ed7b4f4`), so none of these routes exists.
`usb_boot_stick_pulled` is deleted (`02366d741`).

One way in was found and closed in the primitive: a wake popped a waiter and
cleared its flag in two steps, so a waiter withdrawing between them found its
own flag still set (`aaddf38a^:toyos-sched/loom/tests/loom_ticket.rs`,
`cancel_and_wake_agree_on_who_won`). That was not shown to be the sightings'
path, and nothing has reproduced the panic since.

**Exit**: either a reproduction on the current watch, accounted for by the
wait that left the flag set and fixed there, or a loom model of
`Watch::register` and `unregister` over every exit of a wait (commit, cancel,
revoke) that shows each clears the flag, with a mutation that leaves it set
going red.
107 changes: 0 additions & 107 deletions issues/io-uring-enter-trips-the-one-queue-invariant.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ opened: 2026-09-29
# Kernel text is writable, and every kernel page is executable

The kernel maps the whole direct map `PRESENT | WRITE` with no `NX`
(`kernel/src/arch/x86_64/paging.rs:946-949`), and the kernel image runs from
(`kernel_leaf`, which `paging::init` maps every 2 MiB with,
`kernel/src/arch/x86_64/paging.rs`), and the kernel image runs from
it, at `PHYS_OFFSET` plus its physical address. Its text can be overwritten,
and its heap, its stacks and every user page's direct-map alias can be
executed at CPL 0. Linux at `Ubuntu-6.8.0-142.142`, under the T14's
Expand Down
19 changes: 0 additions & 19 deletions issues/kernel_stack_addr-names-an-offset-not-an-address.md

This file was deleted.

12 changes: 8 additions & 4 deletions issues/libc-answers-einval-for-every-error-it-does-not-name.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ opened: 2026-10-01

# libc answers EINVAL for every error it does not name

Read from the code, not run. `set_errno` (`sdk/libc/src/posix_io.rs`)
Read from the code, not run. `errcode::of` (`sdk/libc/src/errcode.rs`), which
`posix_io.rs`'s `set_errno` asks,
sends `Unknown`, `BadAddress`, `ResourceExhausted` and `NotSupported` through
`_ => EINVAL`, so each reaches a C caller as `EINVAL`, which POSIX spells
"invalid argument". A full handle table (`kernel/src/object/handle.rs`)
answers `dup`, `open` and `pipe` `EINVAL` where POSIX has `EMFILE`, a full
"invalid argument". libc's own descriptor table answers `EMFILE` when it is
full (`fd_errno`, `sdk/libc/src/posix_io.rs`), but a full kernel handle table
(`kernel/src/object/handle.rs`) refuses the `open` or `pipe` beneath it first
with `ResourceExhausted`, which reaches the caller as `EINVAL` where POSIX has
`EMFILE`; a full
bcachefs volume (`kernel/src/bcachefs_adapter.rs`) `write` `EINVAL` where it
has `ENOSPC`, and an operation a filesystem lacks `EINVAL` where it has
`ENOTSUP`. A refusal that is truly `EINVAL` reads the same as each of them, so
a case asserting `EINVAL` passes whichever happened.

**Exit**: `set_errno` matches every `SyscallError` by name, with no catch-all,
**Exit**: `errcode::of` matches every `SyscallError` by name, with no catch-all,
each call that can meet `ResourceExhausted` answering the POSIX errno its own
page names for it, and a host test holds each call's mapping.
8 changes: 4 additions & 4 deletions issues/libc-answers-epipe-and-raises-no-sigpipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ POSIX's `write` to a pipe or socket whose reader is gone, and its `send` on a
stream that can send no more, fail `EPIPE` and also send the calling thread
`SIGPIPE`, whose default action ends the process; `send` raises none only with
`MSG_NOSIGNAL`. ToyOS's libc fails `EPIPE` and raises nothing
(`sdk/libc/src/posix_io.rs`, `SyscallError::Gone`): it has no signals
(`signal`, `sigaction` and `raise` in `sdk/libc/src/misc.rs` do nothing),
and none of those
(`sdk/libc/src/errcode.rs`, `SyscallError::Gone`): it raises no signal but
`SIGALRM` (`signal` and `sigaction` in `sdk/libc/src/misc.rs` act only on that
one, and `raise` does nothing), and none of those
`issues/a-childs-end-is-an-event-and-a-parent-takes-its-children-down.md`
has it imitate is `SIGPIPE`. So every write behaves as if `MSG_NOSIGNAL` were
given, and a C program that relies on the default action to stop writing into a
Expand All @@ -24,7 +24,7 @@ others, is a decision of libc's design that nobody has made.

Read from the code, not measured.

**Exit condition**: the decision is made and recorded at `posix_io.rs`, and a
**Exit condition**: the decision is made and recorded at `errcode.rs`, and a
guest C case asserts it on both paths, a `write` into a pipe whose reader is
gone and a `send` after `shutdown(SHUT_WR)`: with `SIGPIPE` raised, the default
action ends the process, an ignored one leaves `EPIPE` and `MSG_NOSIGNAL`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ the host has, and the verdict differs between a Linux host and a macOS host:
`getsockname`, with `darwin_reads` for where macOS parts from Linux.
- `socket_options.rs`: `setsockopt` and `getsockopt` on real host sockets, with `darwin` for where
macOS parts from Linux.
- `fparts_differential.rs`: `modf` and `logb`.
- `fparts_differential.rs`: `modf`, `round`, `lround` and `logb`.
- `text_differential.rs`: `strnlen` (its `strsignal` half is held to a committed glibc table and
conforms).

Expand Down
38 changes: 0 additions & 38 deletions issues/libc-loses-the-kernels-word-on-three-write-paths.md

This file was deleted.

27 changes: 27 additions & 0 deletions issues/libc-loses-the-kernels-word-on-two-write-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
status: open
kind: defect
opened: 2026-08-31
---

# `sendto`, `fwrite` and `fread` throw the kernel's error away

`sdk/libc/src/posix_io.rs`'s `write(2)` passes the kernel's `SyscallError` to
`set_errno`, so a write whose reader is gone is `EPIPE` and a full non-blocking
pipe is `EAGAIN`, and `send`'s stream arm answers through `stream_errno`
(`sdk/libc/src/socket.rs`). Two sibling paths do not:

* `sdk/libc/src/socket.rs`, `Datagrams::send` for `SocketEntry`, under
`sendto`'s UDP arm: the write into netstack's pipe is
`.map_err(|_| EIO)`. Every refusal is `EIO`, including netstack gone, which
the kernel answers `Gone` and `write(2)` reports as `EPIPE`.
* `sdk/libc/src/stdio.rs`, `sys_write` and `sys_read`: a refused write or read
answers `-1` and sets **no** `errno`, so a failed `fwrite`, `fputs` or
`fread` leaves whatever the last failing call left behind.

## What closing it takes

The datagram arm answers the kernel's `SyscallError` through `errcode::of`,
whose mapping the host already tests, so `EIO` is the answer only where the
kernel said `SyscallError::Io`. `sys_write` and `sys_read` already hold an
`Errno` (`write_fd` and `on_fd`, `sdk/libc/src/posix_io.rs`) and set it.
14 changes: 7 additions & 7 deletions issues/libc-munmap-refuses-part-of-a-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ told `EINVAL` and keeps all of what it mapped. Nothing it kept is taken.

The kernel is why: a mapping is one `vma::Region` over one `PageAlloc`, with
no part to take away, and `SYS_MUNMAP` takes only the length the mapping's
`mmap` was asked for (`kernel/src/syscall/vm.rs`). That is the ruling in
`mmap` was asked for (`kernel/src/syscall/vm.rs`). That was the ruling in
`issues/process-memory-is-2-mib-pages-and-that-caps-the-process-count.md`'s
stage 4.
stage 4, which the owner has since revised (below).

`mmap` with `MAP_FIXED` has the same gap. POSIX replaces whatever pages
`[addr, addr + len)` overlaps, of any mappings, and leaves the rest of each
Expand All @@ -52,11 +52,11 @@ corpus, and not across what a user builds against this libc.
`206_libc_refusals` reads 0 for `munmap` of the first of two pages, the second
page still holds its byte, and a touch of the first faults; and its
`mmap the first of two pages fixed` line reads 0, with the second page's byte
kept after it. Or the owner rules the refusals final and this file becomes
`kind: rejected`.
kept after it.

## Owner

The orchestrator, who holds the 2 MiB track and ruled the refusal in its
stage 4. The first exit needs a region that splits, and no stage of that
track builds one.
The orchestrator, who holds the 2 MiB track. The owner ruled on 2026-10-21
that whole-mapping-only unmap and replacement is not the final application
contract, and that the track's refusal of partial unmaps is to be revised. The
exit needs a region that splits, and no stage of that track builds one yet.
11 changes: 3 additions & 8 deletions issues/libc-refuses-what-toyos-cannot-yet-answer.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,9 @@ These answer failure in their POSIX form and do nothing
maps neither.
- `madvise`'s `MADV_DONTNEED`, `ENOSYS`: no call discards a range's pages and
keeps it mapped, so it cannot read back as zeros as Linux's does.
- `fcntl`: a record lock, `EINVAL`, POSIX's answer for a file that supports no
locking; `F_DUPFD_CLOEXEC`, `F_GETFL`, `F_SETFL`, `F_GETOWN` and `F_SETOWN`,
`ENOSYS`. `F_DUPFD` answers a duplicate at or above its argument, the first
the kernel hands `dup`, where POSIX has the lowest free number, and refuses
`EINVAL` an argument at or above the slots a handle table has
(`RawHandle::MAX_SLOTS`). `F_GETFD` and `F_SETFD` of a descriptor the
process does not hold answer 0, where POSIX has `EBADF`: the kernel ends a
process that names a handle it does not hold, so libc asks it nothing.
- `fcntl` (`sdk/libc/src/fdreq.rs`): a record lock, `EINVAL`, POSIX's answer
for a file that supports no locking; `F_GETFL`, `F_SETFL`, `F_GETOWN` and
`F_SETOWN`, `ENOSYS`.

Ruled out, and owed nothing while the ruling stands: `execv` and `execve`, no
call replacing a process's image; `setsid` and `getsid`, no POSIX session;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ count:
So on `main` the arm created 32,769 files through the DATA server (364 s and
442 s of `readdir_bound` on #536's two whole runs, against 46-181 s before it)
to reach an answer an empty `/home` gives too. The arm is deleted; what is left
untested is the one refusal the DATA server has for a listing too large, the
`WINDOW_BYTES` check in `userland/fsd/src/main.rs`'s `READDIR`.
untested is the one refusal the DATA server has for a listing too large,
`Error::LimitExceeded` past `WINDOW_BYTES` in `system/fileserver/src/main.rs`'s
`READDIR`. Whether listing `/home` itself still answers `NotFound` has not been
read since #536's branch.

## Owner

Expand All @@ -30,5 +32,5 @@ The storage track, which moved `/home` behind the DATA server.
## What would close it

`read_dir("/home")` on a machine with a DATA volume answers `/home`'s entries,
and a guest test lists a served directory past `WINDOW_BYTES` and is refused
with `ResourceExhausted` by name rather than by any error.
and a test lists a served directory past `WINDOW_BYTES` and is refused with
`LimitExceeded` by name rather than by any error.
Loading