Repository navigation
fd: one descriptor table numbers every raw fd, and a child's descriptors are its fd:<n> endowments - #978
Conversation
|
Metal, T14 (ThinkPad T14 Gen 2), head |
…7c60bc1bd; masked
Review of #978 r1 (head
|
… and costs no data Review r1 of #978. An fd:<n> label is read as canonical decimal only, so "fd:+3" and "fd:03" name nothing. An endowment whose number is already open is refused at the boundary and its handle closed, where an assert used to panic under the table's lock: the panic's message goes to stderr, a descriptor, and the same thread waited on the lock forever. The table's own operations no longer panic either. They answer a broken table as a fault, which the owner panics on once the lock is released. The table's empty state is all zeros, so it lands in .bss: a descriptor's word is its object's index plus one with a spare bit for the flag, and an object with no references is free. net::stream's tables, which every program now links through the table's release of a handle, are made all-zero the same way. calc's .data is back to the base's 0x16a8 bytes from 0x356c0. toyos::fd::copy refuses a stream's end by the handle the descriptor names. The check ran on the fresh duplicate, which is never a stream, so a stream dup2'd onto stdio was passed to a child. std's File child copy goes through the same check. libc's posix_spawn refuses a descriptor it holds but cannot give, where it started the child without it. Also: - a closed stdin reads its end without a probe that raced a close; - libc's poll answers POLLNVAL for a descriptor that is not open; - libc sockets carry a tag disjoint from socket2's; - libc's table-size constants are toyos::fd::MAX_FDS; - clippy lints the fd-table owner. socket2 now makes sockets not inherited and reuses keys (2aa6b0049e70). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…25dfdf586a29: 7 fd table controls, libc copies, --ci host, both builds, 12 QEMU rows, base and head section sizes, metal staging with a stream control; masked
|
Round 2 ( B1 (fixed).
B2 (fixed).
B3 (confirmed, then fixed). Measured with
B4 (fixed). New shape B5 and B6 (fixed in socket2,
Optionals:
Also:
|
r2 (head
|
|
Metal, T14 (ThinkPad T14 Gen 2), head
|
…ors are its fd:<n> endowments A raw fd on ToyOS was a kernel handle, so C's small descriptor numbers, std's raw-fd interop and POSIX semantics (lowest-free numbering, dup sharing an object, dup2 answering its target, close-on-exec per descriptor) had no home, and libc kept close-on-exec marks, a socket table numbered from 1024 and a dup2 that could not answer POSIX's number. toyos::fd is the process's one descriptor table, owned by the copy of `toyos` whose `fd-table` feature is on: std's (rustc-dep-of-std) in a Rust program, libc's own (`fd-owner`, its C-only build) in a C program, every copy reaching it through `toyos_fd_*`. A descriptor names an object, a kernel handle or an object a library keeps itself, numbered lowest-free with an inheritable flag of its own; a call holds its object, so a racing close makes the next lookup BadFd and never a call on a closed handle. std's File, pipes, stdin and network types are table descriptors, as on unix: as_raw_fd, into_raw_fd and from_raw_fd move descriptor numbers, and OwnedFd closes and duplicates through the table (the rust fork's arms). `std::os::toyos::io::AsRawHandle` answers the kernel handle behind a descriptor, for ToyOS-only code and the mio fork's selector; a portable program needs none. A closed stdin reads its end and a closed stdout drops its bytes, as std on unix does. libc's every descriptor call is on the table: open, pipe, dup, dup2 (answering its target), fcntl's close-on-exec and F_DUPFD_CLOEXEC, and sockets, which are objects of its own (a close now releases one; the close-socket issue keeps its last exit, a C case). The socket2 fork's sockets converge the same way, and its try_clone is a dup. A child's descriptors, stdio included, travel as endowments labelled `fd:<n>`, which its table installs at their numbers at its first use. The kernel's spawn slot map, SYS_HANDLE_DUP_SLOT and SYS_HANDLE_DUP_AT go, and with them HandleTable's slot-placing paths; an endowed console is minted for the child as the slot map's was. The launch protocol's stdio slots are descriptors 0 to 2, which the supervisor endows. The supervisor's own stdio is the kernel's three fd: endowments, its log ring put at 1 and 2 by the table's dup2; relocation's refusal line finds fd:2 in the endowment table. Guest tests moved off slot numbers and RawHandle(0..2); a C child asking about a descriptor it lacks is answered EBADF, as POSIX has it, rather than ended by a handle fault, so spawn_signaled loses its handle arm (the status mapping stays host-tested). The capability record, the children's issue and the stream issue say what the spawn now carries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and costs no data Review r1 of #978. An fd:<n> label is read as canonical decimal only, so "fd:+3" and "fd:03" name nothing. An endowment whose number is already open is refused at the boundary and its handle closed, where an assert used to panic under the table's lock: the panic's message goes to stderr, a descriptor, and the same thread waited on the lock forever. The table's own operations no longer panic either. They answer a broken table as a fault, which the owner panics on once the lock is released. The table's empty state is all zeros, so it lands in .bss: a descriptor's word is its object's index plus one with a spare bit for the flag, and an object with no references is free. net::stream's tables, which every program now links through the table's release of a handle, are made all-zero the same way. calc's .data is back to the base's 0x16a8 bytes from 0x356c0. toyos::fd::copy refuses a stream's end by the handle the descriptor names. The check ran on the fresh duplicate, which is never a stream, so a stream dup2'd onto stdio was passed to a child. std's File child copy goes through the same check. libc's posix_spawn refuses a descriptor it holds but cannot give, where it started the child without it. Also: - a closed stdin reads its end without a probe that raced a close; - libc's poll answers POLLNVAL for a descriptor that is not open; - libc sockets carry a tag disjoint from socket2's; - libc's table-size constants are toyos::fd::MAX_FDS; - clippy lints the fd-table owner. socket2 now makes sockets not inherited and reuses keys (2aa6b0049e70). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cbe0cae to
249293c
Compare
…d840d917d85cd2c10587: --ci host, both builds, QEMU rows; masked
|
Rebased onto The #971 conflicts were mechanical. Each takes #971's side and keeps #978's intent:
#980 rebased cleanly. Checks:
Evidence: https://github.com/ToyOSOrg/evidence/tree/2a0e7277e8881439f6b8be3306e997037c6ccc33/r2-rebase The T14 17/17 at |
A one-off accuracy audit of 117 open defect records (batch 0 of 4). Each claim was checked against origin/main at 6f4d8db and the owner's uncommitted planning in the primary checkout, then brought onto db0cf63 (#971, #978, #980) and the records whose cited code #971 removed checked again there. The per-record classification, with file:line evidence, is in the PR. Deleted as fixed. For each, which halves of the exit were met, and which were judged unnecessary and why: - a-file-server-killed-mid-sync-leaves-a-half-written-btree: met whole. 3adcf42 made DATA's commit shadow paging, the superblock written last after a flush, and its host test, lib/bcachefs/tests/crash.rs, stops a commit at every block write, torn sectors and a lost block 0 included, and mounts what is left: the exit's own test. The residual defects are owned by a-write-over-a-files-committed-page-is-not-shadowed and a-crash-leaks-the-blocks-of-its-last-commit. - a-long-name-entry-whose-write-was-refused-is-on-the-volume-and-the-create-erases-nothing: the QEMU half is met by a cheaper test, the T14 half judged unnecessary. 0d73b99 removed erase_inserted; write_entry_at queues the old bytes as a repair before every write, so an entry write refused after it landed is written back. lib/fat32/tests/refused_writes.rs models the device the exit asked QEMU to stage, a write that lands and is answered with an error, as a host test: `cargo test -p toyos-fat32 --test refused_writes every_refused_write_of_a_long_name_create_is_undone` exit 0 at 6f4d8db, 24 refused and landed cases on the record's own file name, each checked by toyos-fat32-check. The T14 fat check of a usb-transport-break boot was not run: the mechanism is the crate's and the host model reaches every write, and since #971 that actuator no longer exists, so the arm cannot be staged. - a-replacement-netstack-reuses-its-predecessors-ports: met. Since 4b96de0 netstack runs ToyOS's own stack, whose TCP ephemeral ports are RFC 6056 keyed draws from keys each process takes from the system's randomness, the remedy the record named; NetDaemon's fixed 49152 is gone. Its citation in netstack-ends-for-the-boot-on-a-nic-it-cannot-drive-on.md goes with it. - a-disk-whose-port-went-away-panics-the-boot-at-roots-hold was fixed by 602cff0: its exit's code half is met, and its "green on a nightly" half by that commit's three local `--nightly` runs of usb_transport_break with a reverted negative control, not by a CI nightly. #971 deleted the record on main first, so this change no longer carries the deletion. Deleted as superseded: - a-deliberate-wedge-inside-a-driver-panics-every-other-cpu-that-wants-its-lock: the usb-wedge-* actuators it was about went in 29e1006, and 13ed7b4 (#971) took the kernel's USB driver out and its restoration out of stage E of the-guest-suite-runs-only-what-no-cheaper-tier-reaches.md. The one staged wedge left, wedge-before-reset, holds no lock when it stops. Corrected where the text no longer matched the code, the defect kept: renamed programs (init, netd, logd, fsd, soundd), moved functions and files, line numbers that drifted, guest tests cut since, citations of deleted files, and statements the tree refutes (the console's blocking read, the DMA address window's arithmetic, the mio half of the TCP_NODELAY record, the firmware-call record's account of when AML writes first reached the kernel, d2006cc, and its widening, 15536e2). On the new base the console-watch and disk-offline records are main's text, the console's carrying the residual correction to its blocking read; held-disk, the NVMe boot wait and reset-stops-xHCI are main's deletions, and the job-list record's citation of the kernel's xHCI stop now names pcidev::stop_before_reset. Left untouched because the owner's uncommitted planning edits them: a-driver-that-reflashes-its-device-escapes-its-isolation, a-file-held-across-a-file-servers-restart-answers-gone, a-machine-without-an-iommu-refuses-every-claim, and a-memory-overwrite-request-ubuntu-left-set-stays-set-under-toyos, which the owner deleted locally as superseded (claude-questions.md, item 12) together with its citation in the loader-wipe issue. Also removes a home-directory path from a-job-list-hangs-with-interrupts-on-and-the-deadline-ends-it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-off accuracy audit of batch 1 (117 open `kind: defect` records). Each claim was checked against origin/main at 6f4d8db. Every record that #971, #978 and #980 touched was then re-audited against db0cf63, the base this lands on. The primary checkout's uncommitted planning (requirements/, claude-questions.md) was read and not edited. Counts at db0cf63: 2 fixed, 43 partly stale, 60 still valid, 8 uncertain, and 4 already deleted by #971 (hotplug-blocks-a-scheduler- pass, a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown, an-acknowledge-after-an-enumeration-clears-a-replug-no-look-has-seen, an-xhci-storm-starves-the-cpu-that-takes-it). Still-valid and uncertain records are unchanged, except for the two extended below. Deleted, fixed: - a-second-launch-of-a-serves-program-panics-the-supervisor: no launcher client can reach the panic. `may_start` refuses a target the caller's `starts` does not list (4e20480). The build refuses a `starts` entry naming a row that serves a port or a role (`starts_name_what_a_launch_can_start`, host-tested), and a package's synthesized row serves nothing. The panic is now an internal invariant; a-comment-says-a-second-launch-is-refused-where-the- supervisor-panics records the comment above it, which still says "refused". - bus-mastering-rides-memory-decode: #971 removed the kernel xHCI driver, the last kernel site that armed bus mastering before its refusals. virtio arms in `accept` after `offer` has parsed; amdvi arms after programming, with no refusal after it; a claimed function starts mastering through its own driver's `dma_start`, after its parse (usbd, nvme). build/sourcegate.rs's `BUS_MASTER_SITES` comment no longer cites the record. Renamed, the slug's claim refuted, a residual kept, every citation moved: - a-safe-port-read-reaches-any-io-port -> a-safe-port-read-reaches-any- declared-port: a `pio::Port` is made only from a declared run, but the fixed runs are pub consts, so a safe read still bypasses `serial::Registers`. - a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can-take- it -> no-row-holds-a-wake-beside-seven-spinners-to-100-us. #874's `wake_onto_busy` read p99 5 us (T14) and 25 us (AMD bench), with 200 of 200 wakes run off the busy CPU. What is left is the 7-spinner arm's 100 us bound, which no row holds. The 8-spinner arm moves to a-woken-thread-waits-out-a-sibling-spinners-quantum-when-every-cpu-is- busy, its owner. The speed track's two citations moved, and its stale "whole 10 ms quantum" line was corrected. - diskserver-resets-a-drive-that-is-slow-and-healthy -> the-nvme-driver- resets-a-drive-that-is-slow-and-healthy: `COMMAND_SILENCE` is the nvme driver's (system/nvme/src/main.rs), a row apart from diskserver. Extended: a-spawn-drops-an-empty-argument now covers the supervisor's `serve_launch`, which drops empty and non-UTF-8 arguments from the same blob; its exit asks for a host test of that decode. Corrected to the current code, residual defect kept: paths and names after the userland moves (fsd -> fileserver, init -> supervisor, soundd -> soundserver, netd -> netstack, blockd -> nvme); line numbers replaced by names; tests cut since filing recorded as cut (consent_prompt in ff1a11d, screen_gop_firmware_mode in 520c0d1). Some mechanisms changed under a record: netring stage 3's stream refusals; the DMA ordering behind `DmaBuffers::publish`/`observe`; blocked-time accounting's one caller; `SpawnArgs::image`; the blackbox tail keeping the newest records; the T14's HDA now driven; and `close_ends_polls`'s arms at db0cf63. a-usb3-device-attached-but-untrained-reads-as-gone takes #971's rewrite as it stands. The per-record table (class, evidence, action) is in the PR's evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-off accuracy audit of batch 1 (117 open `kind: defect` records). Each claim was checked against origin/main at 6f4d8db. Every record that #971, #978 and #980 touched was then re-audited against db0cf63, the base this lands on. The primary checkout's uncommitted planning (requirements/, claude-questions.md) was read and not edited. Counts at db0cf63: 2 fixed, 43 partly stale, 60 still valid, 8 uncertain, and 4 already deleted by #971 (hotplug-blocks-a-scheduler- pass, a-shutdown-on-a-held-usb-disk-left-a-cpu-deaf-to-a-tlb-shootdown, an-acknowledge-after-an-enumeration-clears-a-replug-no-look-has-seen, an-xhci-storm-starves-the-cpu-that-takes-it). Still-valid and uncertain records are unchanged, except for the two extended below. Deleted, fixed: - a-second-launch-of-a-serves-program-panics-the-supervisor: no launcher client can reach the panic. `may_start` refuses a target the caller's `starts` does not list (4e20480). The build refuses a `starts` entry naming a row that serves a port or a role (`starts_name_what_a_launch_can_start`, host-tested), and a package's synthesized row serves nothing. The panic is now an internal invariant; a-comment-says-a-second-launch-is-refused-where-the- supervisor-panics records the comment above it, which still says "refused". - bus-mastering-rides-memory-decode: #971 removed the kernel xHCI driver, the last kernel site that armed bus mastering before its refusals. virtio arms in `accept` after `offer` has parsed; amdvi arms after programming, with no refusal after it; a claimed function starts mastering through its own driver's `dma_start`, after its parse (usbd, nvme). build/sourcegate.rs's `BUS_MASTER_SITES` comment no longer cites the record. Renamed, the slug's claim refuted, a residual kept, every citation moved: - a-safe-port-read-reaches-any-io-port -> a-safe-port-read-reaches-any- declared-port: a `pio::Port` is made only from a declared run, but the fixed runs are pub consts, so a safe read still bypasses `serial::Registers`. - a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can-take- it -> no-row-holds-a-wake-beside-seven-spinners-to-100-us. #874's `wake_onto_busy` read p99 5 us (T14) and 25 us (AMD bench), with 200 of 200 wakes run off the busy CPU. What is left is the 7-spinner arm's 100 us bound, which no row holds. The 8-spinner arm moves to a-woken-thread-waits-out-a-sibling-spinners-quantum-when-every-cpu-is- busy, its owner. The speed track's two citations moved, and its stale "whole 10 ms quantum" line was corrected. - diskserver-resets-a-drive-that-is-slow-and-healthy -> the-nvme-driver- resets-a-drive-that-is-slow-and-healthy: `COMMAND_SILENCE` is the nvme driver's (system/nvme/src/main.rs), a row apart from diskserver. Extended: a-spawn-drops-an-empty-argument now covers the supervisor's `serve_launch`, which drops empty and non-UTF-8 arguments from the same blob; its exit asks for a host test of that decode. Corrected to the current code, residual defect kept: paths and names after the userland moves (fsd -> fileserver, init -> supervisor, soundd -> soundserver, netd -> netstack, blockd -> nvme); line numbers replaced by names; tests cut since filing recorded as cut (consent_prompt in ff1a11d, screen_gop_firmware_mode in 520c0d1). Some mechanisms changed under a record: netring stage 3's stream refusals; the DMA ordering behind `DmaBuffers::publish`/`observe`; blocked-time accounting's one caller; `SpawnArgs::image`; the blackbox tail keeping the newest records; the T14's HDA now driven; and `close_ends_polls`'s arms at db0cf63. a-usb3-device-attached-but-untrained-reads-as-gone takes #971's rewrite as it stands. The per-record table (class, evidence, action) is in the PR's evidence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#978 (the descriptor table) and this PR both edited a-stream-cannot-move-between-processes. The merged text is this PR's account of the refusals stage 3 landed, written in #978's descriptor terms: the spawn refusal now runs through `toyos::fd::copy` and `sdk/std/sys/pal/mod.rs`. #978 also changed std's panic for a served file's raw fd to "has no descriptor", so a-served-file-panics-when-asked-its-raw-fd quotes it and asks for a descriptor. Batch 0 (#981) edited no record this PR touches. The remaining #978-adjacent citations hold at a8ea09d: the kernel's `SYS_SPAWN` argv filter, the supervisor's `serve_launch` filter, test-runner's `SysCap::duplicate` endowment, libc's `open` through `syscall::open`, and fds 1 and 2 through `toyos::log::stdio`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-off accuracy audit of 117 open defect records. Each was read against the code it cites at origin/main db0cf63, with the owner's uncommitted requirements/ and claude-questions.md beside it. After rebasing onto a8ea09d (#978, #981), each record whose cited code #978 changed was read again. Deleted, fixed: - netstack-watches-both-pipes-of-every-connection-on-every-pass: c91e72b gave a pipe end's leaving its own watch (kernel/src/pipe.rs) and made netstack submit only a changed or answered watch (watches.rs `Armed`). The commit named the record and left it. - shell-c-discards-the-cwd-its-launch-carried: `shell -c` runs in its launch's directory since b3770cf. - std-answers-every-spawn-refusal-but-notfound-as-other: std's spawn maps each refusal by name, and every SyscallError through to_io_error, since 61a283a. Deleted, superseded (the subject's code or test no longer exists): - os-toyos-io-traits-keep-a-posix-name: #978 makes a RawFd a number in the process's one descriptor table, as on unix, and gives the kernel handle its own word, std::os::toyos::io::AsRawHandle. - kernel_stack_addr-names-an-offset-not-an-address: #583 merged without the rename; the rename is a step of the-loader-does-only-what-must-precede-the-handover.md. - log-flush-retry-deadman-arm: the kernel's fsync deadman went with the durability ledger (80a1f1c), its kernel USB with usbd (13ed7b4), and the test with 9ebf080. - metal-device-probe-usbread-answered-iofailed-once-beside-other-guests: one sighting through the kernel's usb-storage, gone with 13ed7b4; test deleted 24aa318. - netstack-refused-accept-hung-waiting-for-a-wake-that-never-came: netd and its listen.rs are gone, the test was deleted (7967fba), and so is netcase_against_host. - process-stats-exits-101-beside-other-guests: both recorded reds were the test's own premise; the test is deleted (4a5b228). - screen-diag-boot-leaves-no-i8042-line-on-the-panel: the kernel's i8042 and its health line left for ps2server (febfd21); test deleted. Corrected and kept, each with what remains: - io-uring-enter-trips-the-one-queue-invariant becomes a-wait-was-registered-while-its-task-still-read-waiting: the assertion is now Watch::register's; every recorded route is gone; the cause never was found. - libc-loses-the-kernels-word-on-three-write-paths becomes ...-two-...: send's stream arm answers through stream_errno; sendto's datagram arm and stdio's sys_write still drop the kernel's word. - logkeeper-flushes-the-records-its-own-refused-flush-made becomes logkeeper-flushes-the-records-its-own-flush-made: the measured kernel retry is gone; whether any flush now makes records is unmeasured. - std-calls-undated-what-it-did-not-stat becomes std-set-times-answers-ok-and-sets-nothing: the mtime half is fixed. - remove-dir-all-empties-a-directory-and-leaves-it: std's is fixed; pkg's remove_tree still gives that as its reason. - netstack-holds-a-pending-request-for-a-client-that-left: the connect half is gone; the waiting receive is still unwatched. - netstack-keeps-receiving-for-a-client-that-closed-its-receive-end, netstack-socket-ids-are-ambient, netstack-lookup-slots-have-no-per-client-share, netstacks-places-are-one-number-for-every-client, most-t14-leases-land-one-dhcp-retry-late: restated for regions, badges and netstack. - smp-ap-hole-and-log-reserve-window-red-under-a-loaded-host: the ROOT read half is gone; the first spawn's WouldBlock is kept. - soundserver-past-due-wake-max-1: only the null sink's wait still does it. - std-says-a-launch-moves-its-handles-...: the std comment moved into toyos::spawn, where it is right; provide's doc is still wrong. - std-maps-a-device-error-to-other-not-uncategorized: the consumers that kept Other load-bearing are gone. - sharedmemory-slices-rest-on-a-size-nobody-checks: a peer's region maps through `lent`, which checks it; `adopt` does not. - sshserver-authorized-keys-unprotected: installed packages no longer see /state; every shipped program still does. - libc-munmap-refuses-part-of-a-mapping: the owner's 2026-10-21 memory checkpoint revises the refusal, so the "rule it final" exit goes. - libc-refuses-what-toyos-cannot-yet-answer: after #978 F_DUPFD_CLOEXEC, F_DUPFD's lowest free and F_GETFD's EBADF hold; the fcntl bullet keeps record locks and F_GETFL, F_SETFL, F_GETOWN, F_SETOWN. - Sites, names and paths moved for: kernel-text-is-writable..., libc-answers-einval..., libc-answers-epipe..., libc-host-tests..., listing-home..., logkeeper-ends..., map-mmio..., metalprobes-usb-read..., mio-deregister..., no-console..., one-diskserver-session..., one-write-far-past..., probe-mounts-on-a-checksum, sshserver-holds-one-channel..., sshserver-still-speaks-rsa, std-lookup-host..., null-sink-applies-one-connect (annotated, not re-seen). parallel-tests-red-under-other-suites.md loses its pointer to a deleted record. no-physical-memory-fairness is owner-held and untouched; 58 records are still valid and 9 uncertain, both unchanged; four were already deleted on main (#971, #980, #978). The per-record table with file and line evidence is in the pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-off accuracy audit of 117 open defect records. Each was read against the code it cites at origin/main db0cf63, with the owner's uncommitted requirements/ and claude-questions.md beside it. After rebasing onto a8ea09d (#978, #981), each record whose cited code #978 changed was read again. Deleted, fixed: - netstack-watches-both-pipes-of-every-connection-on-every-pass: c91e72b gave a pipe end's leaving its own watch (kernel/src/pipe.rs) and made netstack submit only a changed or answered watch (watches.rs `Armed`). The commit named the record and left it. - shell-c-discards-the-cwd-its-launch-carried: `shell -c` runs in its launch's directory since b3770cf. - std-answers-every-spawn-refusal-but-notfound-as-other: std's spawn maps each refusal by name, and every SyscallError through to_io_error, since 61a283a. Deleted, superseded: - kernel_stack_addr-names-an-offset-not-an-address: the field is still `kernel_stack_addr` (sdk/abi/src/boot.rs:9), and #583, its named owner, merged without the rename. The rename is a stated step of the-loader-does-only-what-must-precede-the-handover.md, which carries it. - os-toyos-io-traits-keep-a-posix-name: the traits still exist, and now name a real descriptor. The owner's 2026-10-21 answers supersede the 2026-08-19 "fds belong only in libc jargon" ruling (64f806d) the record rested on: keep C numbering and dup2 conventions in userland, including Rust/C descriptor interoperability; Rust std's C-style descriptor interoperation uses the same userland adaptation; one descriptor-inheritance rule honored by both libc spawning and Rust Command. #978 implements that, and the kernel handle has its own word, std::os::toyos::io::AsRawHandle. - logkeeper-flushes-the-records-its-own-refused-flush-made: the measured loop was the kernel's retry of a budget-refused fsync, whose four records made logd's next round flush again. That retry and its records went with the durability ledger (80a1f1c) and the kernel's USB storage (13ed7b4). logkeeper ends its volume on a refused append or flush (system/logkeeper/src/main.rs:625-632) and warns of a slow round once, behind its `degraded` latch (:633-643); the file server logs only an operation that failed (system/fileserver/src/data.rs:136, 461), and a failed flush ends the volume. No current route is known. The rest have code or a test that no longer exists: - log-flush-retry-deadman-arm: the kernel's fsync deadman went with the durability ledger (80a1f1c), its kernel USB with usbd (13ed7b4), and the test with 9ebf080. - metal-device-probe-usbread-answered-iofailed-once-beside-other-guests: one sighting through the kernel's usb-storage, gone with 13ed7b4; test deleted 24aa318. - netstack-refused-accept-hung-waiting-for-a-wake-that-never-came: netd and its listen.rs are gone, the test was deleted (7967fba), and so is netcase_against_host. - process-stats-exits-101-beside-other-guests: both recorded reds were the test's own premise; the test is deleted (4a5b228). - screen-diag-boot-leaves-no-i8042-line-on-the-panel: the kernel's i8042 and its health line left for ps2server (febfd21); test deleted. Corrected and kept, each with what remains: - io-uring-enter-trips-the-one-queue-invariant becomes a-wait-was-registered-while-its-task-still-read-waiting: the assertion is now Watch::register's; every recorded route is gone; the cause never was found. - libc-loses-the-kernels-word-on-three-write-paths becomes ...-two-...: send's stream arm answers through stream_errno; sendto's datagram arm and stdio's sys_write and sys_read still drop the kernel's word. - std-calls-undated-what-it-did-not-stat becomes std-set-times-answers-ok-and-sets-nothing: the mtime half is fixed. - remove-dir-all-empties-a-directory-and-leaves-it: std's is fixed; pkg's remove_tree still gives that as its reason. - netstack-holds-a-pending-request-for-a-client-that-left: the connect half is gone; the waiting receive is still unwatched. - netstack-keeps-receiving-for-a-client-that-closed-its-receive-end, netstack-socket-ids-are-ambient, netstack-lookup-slots-have-no-per-client-share, netstacks-places-are-one-number-for-every-client, most-t14-leases-land-one-dhcp-retry-late: restated for regions, badges and netstack. - smp-ap-hole-and-log-reserve-window-red-under-a-loaded-host: the ROOT read half is gone; the first spawn's WouldBlock is kept. - soundserver-past-due-wake-max-1: only the null sink's wait still does it. - std-says-a-launch-moves-its-handles-...: the std comment moved into toyos::spawn, where it is right; provide's doc is still wrong. - std-maps-a-device-error-to-other-not-uncategorized: the consumers that kept Other load-bearing are gone. - sharedmemory-slices-rest-on-a-size-nobody-checks: a peer's region maps through `lent`, which checks it; `adopt` does not. - sshserver-authorized-keys-unprotected: installed packages no longer see /state; every shipped program still does. - libc-munmap-refuses-part-of-a-mapping: the owner ruled on 2026-10-21 that whole-mapping-only unmap is not the final contract and the refusal is to be revised, so the "rule it final" exit goes. - libc-refuses-what-toyos-cannot-yet-answer: after #978 F_DUPFD_CLOEXEC, F_DUPFD's lowest free and F_GETFD's EBADF hold; the fcntl bullet keeps record locks and F_GETFL, F_SETFL, F_GETOWN, F_SETOWN. - Sites, names and paths moved for: kernel-text-is-writable..., libc-answers-einval..., libc-answers-epipe..., libc-host-tests..., listing-home..., logkeeper-ends..., map-mmio..., metalprobes-usb-read..., mio-deregister..., no-console..., one-diskserver-session..., one-write-far-past..., probe-mounts-on-a-checksum, sshserver-holds-one-channel..., sshserver-still-speaks-rsa, std-lookup-host..., null-sink-applies-one-connect (annotated, not re-seen). parallel-tests-red-under-other-suites.md loses its pointer to a deleted record. no-physical-memory-fairness is owner-held and untouched; 58 records are still valid and 9 uncertain (stdio-buffering's deleted-file citation now names its deleting commit), both unchanged; four were already deleted on main (#971, #980, #978). The per-record table with file and line evidence is in the pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each open defect record of batch 3 was checked against origin/main at db0cf63, and every record whose cited code changed up to a8ea09d (#978's descriptor table, #981) was read again there. Seven of the batch were already deleted by 13ed7b4, and tearing-is-what-gop-cannot-give-back carries uncommitted owner edits, so it is untouched. Deleted as fixed, each exit met: - the-ack-delay-abi-doc-names-one-cpu: debug_action::TLB_ACK_DELAY_ARM's doc names each other CPU in turn, the smallest wait answered and the two-second window, as kernel/src/arch/x86_64/tlb.rs does. - the-running-slots-volume-is-claimable: 13ed7b4 deleted the kernel's USB storage path; the kernel serves no partition claim (Claimed holds a class, a PCI function, an ISA row or the ACPI row) and hold_source is gone. What remains, the block service not refusing the running ROOT, is issues/the-block-service-does-not-refuse-the-running-root.md, rejected. - the-sysroots-core-and-alloc-carry-the-compiler-profiles-debug-assertions: 5f73215 set debug-assertions-std = false on its own line in std_config and re-ran the link-time no-panic proof against the built sysroot. - thread-exits-completion-post-is-the-second-one: fa1e325 deleted publish_released, so process::thread_exit's post is the only one. - the-socket2-forks-ownedfd-holds-a-table-key-not-a-handle: #978 gave the process one descriptor table, and socket2 at 2aa6b00, which the lock now pins, installs each socket as a descriptor in it and releases the socket when the last descriptor goes; std's OwnedFd closes through toyos::fd::close. The number OwnedFd holds is the socket's descriptor. Deleted as superseded: - t14-keyboard-will-not-report-its-scancode-set: ps2server (#899) replaced the kernel driver; its probe resets the keyboard with translation on, asks no scancode set and has no firmware-bit fallback, and names undecoded runs. The T14 passed ps2server_arms at a42489c. - the-memory-map-buffer-is-the-unreported-pointer: the framebuffer lies in the boot map's scanout regions now, and the margin is MAP_MARGIN, 64. The memory map's report and the T14 reading it needs are folded into the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it, whose exit already asked for the report. - the-supervisors-claim-of-a-pci-function-the-t14-lacks-holds-preemption- off-for-3-8-ms: #926 removed the measured claim path, as the record itself said. The preemption-off windows it read are syscall-preemption-is-incidental's, whose 131 us mask_windows bound on the T14 covers the inventory read that replaced the claim. - watch-window-spins-out-a-hold-its-poster-is-queued-behind: dc62efc deleted the hold with its actuator. Its one durable fact, that the hold returns with git revert dc62efc, moves into the reference in a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can-take-it. Renamed, the old slug refuted, no inbound reference: - the-panel-seeds-the-loaders-own-file-so-the-gcd-map-is-logged-nowhere -> the-gcd-maps-held-mmio-descriptors-are-logged-nowhere: the console seeds from logkeeper's stream, not /log's files. - the-kernel-depends-on-toyos-fat32-and-uses-none-of-it -> three-comments-say-the-kernel-uses-toyos-fat32: 13ed7b4 removed the dependency; three comments remain. - two-std-maps-still-answer-other-for-a-dead-peer -> std-net-maps-every-other-syscall-error-to-other: TcpStream moved to toyos::net::stream; one map remains, on UdpSocket and the listener. Corrected and kept, each residual defect stated: suite-rows (a guest binary deleted), t14-desktop-froze-at-64s (the drop line's wording), t14-lost-every-integrated-input (ps2server's counters and decoder test), the-t14-mouse-may-be-another-defect (usbd's HID path, no T14 reading), t14-rows (Bench::blocks), test-runner-holds-logread (metalcase alone), the EC record (mediated access, WR_EC for AML), the compiler workspace's toyos-abi lock, the compositor's unknown message, the deadline header's call sites, the pipe lock (user_ptr pins runs; names and lines), the boot map's report_reach list, the VFS's function name, the black box's foreign-record test, the power-off seal (kernel xHCI gone), the copy-out test, the reset word, both shell records, the shared-object cache's exit, the SSH key exchange (toyos-crypto has ML-KEM-768 and its NIST answers), the roster readers, toyos-dns, toybox's row (power is a connector), the click fold, the I219 hang, the lendable region, x2APIC's declaration, the volatile composite site and the file-size ledger. The SO_BROADCAST record's pin is 2aa6b00, where the setter is still a no-op. Inbound references fixed in the-t14-touchpad-is-i2c-hid-and-unbuilt, a-shared-boot-stopped-answering-and-no-capture-says-why, the-diary-computes-no-lateness-and-records-no-slow-system-call and a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can-take-it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each open defect record of batch 3 was checked against origin/main at db0cf63, and every record whose cited code changed up to a8ea09d (#978's descriptor table, #981) was read again there. Seven of the batch were already deleted by 13ed7b4, and tearing-is-what-gop-cannot-give-back carries uncommitted owner edits, so it is untouched. Deleted as fixed, each exit met: - the-ack-delay-abi-doc-names-one-cpu: debug_action::TLB_ACK_DELAY_ARM's doc names each other CPU in turn, the smallest wait answered and the two-second window, as kernel/src/arch/x86_64/tlb.rs does. - the-running-slots-volume-is-claimable: 13ed7b4 deleted the kernel's USB storage path; the kernel serves no partition claim (Claimed holds a class, a PCI function, an ISA row or the ACPI row) and hold_source is gone. What remains, the block service not refusing the running ROOT, is issues/the-block-service-does-not-refuse-the-running-root.md, rejected. - the-sysroots-core-and-alloc-carry-the-compiler-profiles-debug-assertions: 5f73215 set debug-assertions-std = false on its own line in std_config and re-ran the link-time no-panic proof against the built sysroot. - thread-exits-completion-post-is-the-second-one: fa1e325 deleted publish_released, so process::thread_exit's post is the only one. - the-socket2-forks-ownedfd-holds-a-table-key-not-a-handle: #978 gave the process one descriptor table, and socket2 at 2aa6b00, which the lock now pins, installs each socket as a descriptor in it and releases the socket when the last descriptor goes; std's OwnedFd closes through toyos::fd::close. The number OwnedFd holds is the socket's descriptor. Deleted as superseded: - t14-keyboard-will-not-report-its-scancode-set: ps2server (#899) replaced the kernel driver; its probe resets the keyboard with translation on, asks no scancode set and has no firmware-bit fallback, and names undecoded runs. The T14 passed ps2server_arms at a42489c. - the-memory-map-buffer-is-the-unreported-pointer: the framebuffer lies in the boot map's scanout regions now, and the margin is MAP_MARGIN, 64. The memory map's report and the T14 reading it needs are folded into the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it, whose exit already asked for the report. - the-supervisors-claim-of-a-pci-function-the-t14-lacks-holds-preemption- off-for-3-8-ms: #926 removed the measured claim path, as the record itself said. The preemption-off windows it read are syscall-preemption-is-incidental's, whose 131 us mask_windows bound on the T14 covers the inventory read that replaced the claim. - watch-window-spins-out-a-hold-its-poster-is-queued-behind: dc62efc deleted the hold with its actuator. The record's one inbound reference was in a-wake-to-a-busy-cpu-waits-for-its-next-pass-and-no-idle-cpu-can- take-it, which audit batch 1 (#982) deleted, so no reference remains. blocking-read-window-reds-beside-other-guests keeps the pointer that git revert dc62efc restores the hold. The hold's own defect, a 50 ms spin with the task that would post queued behind it on the same CPU, is recorded only here. Renamed, the old slug refuted, no inbound reference: - the-panel-seeds-the-loaders-own-file-so-the-gcd-map-is-logged-nowhere -> the-gcd-maps-held-mmio-descriptors-are-logged-nowhere: the console seeds from logkeeper's stream, not /log's files. - the-kernel-depends-on-toyos-fat32-and-uses-none-of-it -> three-comments-say-the-kernel-uses-toyos-fat32: 13ed7b4 removed the dependency; three comments remain. - two-std-maps-still-answer-other-for-a-dead-peer -> std-net-maps-every-other-syscall-error-to-other: TcpStream moved to toyos::net::stream; one map remains, on UdpSocket and the listener. Corrected and kept, each residual defect stated: suite-rows (a guest binary deleted), t14-desktop-froze-at-64s (the drop line's wording), t14-lost-every-integrated-input (ps2server's counters and decoder test), the-t14-mouse-may-be-another-defect (usbd's HID path, no T14 reading), t14-rows (Bench::blocks), test-runner-holds-logread (metalcase alone), the EC record (mediated access, WR_EC for AML), the compiler workspace's toyos-abi lock, the compositor's unknown message, the deadline header's call sites, the pipe lock (user_ptr pins runs; names and lines), the boot map's report_reach list, the VFS's function name, the black box's foreign-record test, the power-off seal (kernel xHCI gone), the copy-out test, the reset word, both shell records, the shared-object cache's exit, the SSH key exchange (toyos-crypto has ML-KEM-768 and its NIST answers), the roster readers, toyos-dns, toybox's row (power is a connector), the click fold, the I219 hang, the lendable region, x2APIC's declaration, the volatile composite site and the file-size ledger. The SO_BROADCAST record's pin is 2aa6b00, where the setter is still a no-op. Inbound references fixed in the-t14-touchpad-is-i2c-hid-and-unbuilt, a-shared-boot-stopped-answering-and-no-capture-says-why and the-diary-computes-no-lateness-and-records-no-slow-system-call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#978 implemented fcntl's F_DUPFD_CLOEXEC on the descriptor table: the lowest free descriptor at or above the argument, marked close-on-exec, as POSIX has it. 206_libc_refusals still expected the old refusal (-1, ENOSYS), and as a metal-only corpus case nothing ran it before the T14 boot of #988, which answered 10. The record of what libc refuses loses the same stale lines, and F_DUPFD and F_GETFD's departures with them: both are POSIX's now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lowest free descriptor at or above the argument, marked close-on-exec, as POSIX has it. 206_libc_refusals still expected the old refusal (-1, ENOSYS), and as a metal-only corpus case nothing ran it before the T14 boot of #988, which answered 10. The record of what libc refuses loses the same stale lines, and F_DUPFD and F_GETFD's departures with them: both are POSIX's now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner asked whether everything self-hosting needs is written down. toyos-builds-itself.md held it in five lists in an order it called open: what to build, what other tracks block, what stops M2, what M3 adds and what else is owed. It is now one list in dependency order: each item has its status, the issue that owns it and what blocks it, and the evidence it rests on is below it, with libc's revision for the compiles. The status words say only what is true: "direction set" is the owner's ruling of what an item must do, with its design still engineering's. The memory and socket designs, which are outside the tree and under review, are named as such, and so is the sequencing the socket design proposes for the descriptor work's spawn. Folded in: - C reaches served files (#975, #978, #988); - #970's toolchain package; - the memory direction; - the process track's stages 4 to 7; - socket sharing; - the boot handoff, beside the path; - the database feasibility study's libc findings; - the issues that name this track as their owner, among them pread, libc++'s std::filesystem, and the two that keep a build from reproducing. Each owner ruling of 2026-10-21 is recorded once, in its owning issue: - the signal scope in the process track; - sharing rather than moving, in the socket issue, whose exit described a move; - the database rulings in a new track, split from the planning dispositions made under delegation. Four findings get a home: - unlink-while-open; - libc's uid and gid; - sigaction's false success; - st_nlink 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner asked whether everything self-hosting needs is written down. toyos-builds-itself.md held it in five lists in an order it called open: what to build, what other tracks block, what stops M2, what M3 adds and what else is owed. It is now one list in dependency order: each item has its status, the issue that owns it and what blocks it, and the evidence it rests on is below it, with libc's revision for the compiles. The status words say only what is true: "direction set" is the owner's ruling of what an item must do, with its design still engineering's. The memory and socket designs, which are outside the tree and under review, are named as such, and so is the sequencing the socket design proposes for the descriptor work's spawn. Folded in: - C reaches served files (#975, #978, #988); - #970's toolchain package; - the memory direction; - the process track's stages 4 to 7; - socket sharing; - the boot handoff, beside the path; - the database feasibility study's libc findings; - the issues that name this track as their owner, among them pread, libc++'s std::filesystem, and the two that keep a build from reproducing. Each owner ruling of 2026-10-21 is recorded once, in its owning issue: - the signal scope in the process track; - sharing rather than moving, in the socket issue, whose exit described a move; - the database rulings in a new track, split from the planning dispositions made under delegation. Four findings get a home: - unlink-while-open; - libc's uid and gid; - sigaction's false success; - st_nlink 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner asked whether everything self-hosting needs is written down. toyos-builds-itself.md held it in five lists in an order it called open: what to build, what other tracks block, what stops M2, what M3 adds and what else is owed. It is now one list in dependency order: each item has its status, the issue that owns it and what blocks it, and the evidence it rests on is below it, with libc's revision for the compiles. The status words say only what is true: "direction set" is the owner's ruling of what an item must do, with its design still engineering's. The memory and socket designs, which are outside the tree and under review, are named as such, and so is the sequencing the socket design proposes for the descriptor work's spawn. Folded in: - C reaches served files (#975, #978, #988); - #970's toolchain package; - the memory direction; - the process track's stages 4 to 7; - socket sharing; - the boot handoff, beside the path; - the database feasibility study's libc findings; - the issues that name this track as their owner, among them pread, libc++'s std::filesystem, and the two that keep a build from reproducing. Each owner ruling of 2026-10-21 is recorded once, in its owning issue: - the signal scope in the process track; - sharing rather than moving, in the socket issue, whose exit described a move; - the database rulings in a new track, split from the planning dispositions made under delegation. Four findings get a home: - unlink-while-open; - libc's uid and gid; - sigaction's false success; - st_nlink 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draft, PR 2 of 4 in the descriptor work (
fd-design.mdrevision 8; PR 1 was #975). It makes one table number every raw fd in a process, and makes a child's descriptors itsfd:<n>endowments. PR 3 puts served files on the table; PR 4 adds the owner's inheritance rule (Q9), and its seam is the per-descriptor inheritable flag this PR adds.Problem
dupsharing an object,dup2answering its target and per-descriptor close-on-exec had nowhere to live.closeended the process;dup2could not answer POSIX's number.Resulting behaviour
toyos::fd: the process's one descriptor table.toyosowns it, the one whosefd-tablefeature is on. That is std's copy (rustc-dep-of-std) in a Rust program, or libc's own in a C program (fd-owner, its C-only build). Every copy reaches the table only throughtoyos_fd_*.Object::opaque). Descriptors are numbered lowest-free, each with its own inheritable flag.BadFd, never a call on a closed handle. One exception: libc'spolltakes each handle out of its call and waits on it afterwards, so a close racing apollis still a handle fault, as it was before this PR.fd:<n>label is read as canonical decimal only. One whose number is already open is refused at the boundary and its handle closed..bss. So arenet::stream's tables, which every program now links through the table's release of a handle.File, pipes, stdin and the network types are table descriptors, as on unix.OwnedFdcloses and duplicates through the table (rust forka56b4057c).AsRawHandle:std::os::toyos::io::AsRawHandleanswers the kernel handle behind a descriptor. It is the only new ToyOS-specific surface. Console and terminal use it, as does the mio fork's selector; no portable app needs it.toyos::fd::copyrefuses a stream's end by the handle the descriptor names, so a streamdup2'd onto stdio and inherited is refused, as every other spawn path refuses one.File::as_child_stdiogoes through the same check.dup2answers its target, andF_DUPFD_CLOEXECis supported.fcntl's close-on-exec is the table's per-descriptor inheritable flag.closereleases it.issues/libc-close-of-a-socket-is-unproven-on-a-connected-socket.mdkeeps its last exit, a C case. Its tag is disjoint from socket2's.posix_spawnrefuses a descriptor it holds but cannot give a child (a stream's end, or a socket), rather than starting the child without it.pollanswersPOLLNVALfor a descriptor that is not open, as POSIX has it.2aa6b0049e70): its sockets are table descriptors, created not inherited (unix'sSOCK_CLOEXEC), andtry_cloneis adupthat is not inherited either (F_DUPFD_CLOEXEC). A socket's key is reused once it is gone, rather than counted into the tag after 2^24 sockets.fd:<n>, which its table installs at that number on first use.SYS_HANDLE_DUP_SLOT(129) andSYS_HANDLE_DUP_AT(74) are deleted, and so areHandleTable::for_child,install_at,held_atandduplicate_entry.fd:endowments, with its log ring put at 1 and 2 by the table'sdup2.fd:2in the endowment table, using only the stack.EBADF, as POSIX has it, rather than ended by a handle fault.spawn_cloexec.casserts that, andspawn_signaled.closes its handle-fault arm; the status mapping stays host-tested.fd:2exits 127 silently, rather than faulting.Fork commits (append-only branches)
toyos9f34d36cas_raw_handle();bellis aRawHandletoyosd3885019,2aa6b0049e70toyosdependency); not inherited, keys reusedmaina56b4057cOwnedFdcloses and duplicates through the tableCargo.locknames the mio and socket2 revisions and nothing else. A plaincargo updatealso re-resolvedwindows-sys, and I didn't take that. Therustgitlink namesa56b4057c. Hazard: these branch tips need this PR's SDK, so nobody should move a pin to a tip before this lands.Checks (head
cbe0caeef, basee74dbf183)cargo test -p toyos --lib fd::(9 table tests)dupmakes a new object; highest-free numbering; anfd:label past the table; a non-canonical label accepted;install_atoverwrites; a broken table panicscargo test -p toyos-libc-copies(includes a refusedposix_spawnplan)cargo run -- --ci host(includes the new-p toyos --lib --features fd-tableclippy shape)cargo run -- --build-only,--arch aarch64console_image_boots nvme_disk_boots desktop_without_x2apic the_compositor_drives_the_virtio_gpu libc_children a_launch_gives_each_unsent_stdio_slot_a_null bar_map_again machine_shutdown_wire_held machine_shutdown_wire_kept a_drives_row_holds_one_process_per_function hda_plays_to_the_controller_that_reaches_an_output the_opening_exchange_with_qemus_agent_completeslibc_childrenruns the C spawn cases (spawn_cloexecwith its fd 5,popen,/dev/null, pipes) and the newpoll_closed.c(POLLNVAL).Size of the empty table (measured, x86_64,
readelf -SW): calc's.datawas 0x16a8 bytes at base, 0x356c0 at round 1's7c60bc1bd, and is 0x16a8 at head. compositor's goes from 0x4200 to 0x15f8..bssgrows by 0x2ac20 (175 KiB), which the loader demand-pages, so a program touches only the pages it uses.Metal: round 1's T14 run at
7c60bc1bdpassed 17 of 17. Round 2 is staged and not yet run: 3 images of the 16 shared-boot binaries, plus 1 control image. In the control,stream_handlesis built withfd::copy's stream check removed, and its new "inherited stdio" arm is expected to fail.Evidence (masked): round 2 https://github.com/ToyOSOrg/evidence/tree/fb0376c94caf953ac7991068fb581446c0fdc89a/r2; round 1 https://github.com/ToyOSOrg/evidence/tree/a5d8d4f9e95c6614a568842729ed2cd842f81448/r1; round 1 T14 https://github.com/ToyOSOrg/evidence/tree/09440cbd7fb0c8156aadff5022190ad14246d888/r1/metal-run
Not done here
Commandgets its configured stdio and whatinherit_handlenames, andO_CLOEXECandF_SETFDalready set the per-descriptor flag PR 4 will read.toyosoutlives a redirect made through another. A Cdup2onto 1 or 2 in a mixed Rust and C program leaves std writing to the ring it mapped earlier. This predates the table and is recorded inissues/a-stdio-sink-cached-in-one-copy-outlives-a-redirect-in-another.md.posix_spawnnow refuses one withEINVAL, rather than silently starting the child without it. Giving served kinds to a child is PR 4.🤖 Generated with Claude Code