Repository navigation
A child's handles are a list its parent names: labels and namespaces leave the kernel - #1252
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1fa5d42 to
2bbd170
Compare
Review of #1252 at 6aff197 (base main 6d0cb3a), round 1Architectural assessmentThe core change is right and well executed. Naming handles is userland's job, and the kernel now does only what userland cannot: it fixes slot order ( I checked that the kernel no longer names namespaces, endowment labels, Three things need fixing before landing: the provided-connector path in the supervisor, the per-thread charge's coverage claim, and a handful of leftover "namespace" texts. Required (BLOCKER)1. A provided connector without
2. The receiving branch no longer checks the client's provided handle at the launch.
3. The new per-thread charge does not cover what its comment and commit message say it covers.
4. Some "namespace" texts survived the PR's own sweep (f01894f). Each now describes something that no longer exists.
Suggestions (optional)
Settled points the brief asked me to judge
Interfaces other PRs inherit
Evidence
SEND BACK: findings 1–4. Finding 1 needs the orchestrator's decision first. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6aff197 to
d3643d8
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d3643d8 to
041f1b8
Compare
The shared boots and the spawn A/B this change was to be judged by, named with what main must show, for the batched boot of main when a bench returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1252 at b3ac663 (code 041f1b8, base main e10b6fd), round 2Architectural assessmentRound 1's design assessment stands. The fixes are small and in the right places:
The rebase kept #1225's routing exactly. Changes since round 1, excluding main: code +59/−37 (kernel, sdk, system), tests +14/−12, docs and issues +4/−5. I verified the kernel count with Two text-only problems remain. Finding 4 is not fully resolved, and two texts the fix itself wrote are inexact. Round-1 findings1. Forward-without-
2. The client's handle is checked at the launch: resolved.
3. Charge coverage: resolved.
4. Leftover namespace texts: partly resolved. The four texts I named are fixed, along with three image comments and the soundserver policy comment. Others remain; see B1. The three suggestions were taken:
The PR description now says the host-buildable programs were tested, not linted. Rebase: #1225's routingMain's #1225 (e10b6fd) touched only
Required (BLOCKER)B1. Round-1 finding 4 is not complete. These texts still describe the deleted namespace:
B2. Two texts written by the fix for findings 1–2 are inexact:
Both are comment-only changes. Under the guest-run rule they need no new dispatch, and 43092179151 at 041f1b8 stays the evidence. Suggestions (optional)
Evidence
SEND BACK: B1 and B2. Both are text-only, need no new guest run, and the PR then waits only on 43092179151. |
- osbench's IPC rendezvous, the console's surface port, the metal image's `power` connector and std's files module now say what each is: a port's own connector, one provided to the shell, one of the runner's services, and the read-only ancestors of the directory capabilities. - The supervisor's provided-connector comment drops the deleted design's reasoning: a row that does not receive the name gets nothing because its row decides its services. - std's `provide` says the refusal applies only to a child whose row receives the name. - A malformed table's length line names the length word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator verification of the round-2 text fixes (B1, B2, the startup.rs suggestion) at 85de72b: against code head 041f1b8 the only non-comment change is the |
Review of #1252 at f0ef912 (code ec556f5), round 3Scope: the commits since 85de72b. That is ec556f5 (kernel +7/−2, ABI doc +3/−2) and two issue-only commits, d5b1749 and f0ef912 (+70 in two new files). Architectural assessmentThe fix is in the right place and is as small as it can be. The charge stays in The fix: correct
Callers under memory exhaustion
Required (BLOCKER)B1.
Suggestions (optional)S1.
Test for the header-only pathNo test runs a header-only call under exhaustion, and none is needed:
The reds of 43092179151: no path from #1252 found
Evidence
SEND BACK: B1 is text-only. It needs no guest rerun, since it changes only a message and a doc comment in |
…h refusals - `SYS_SYSINFO` checks its live-thread bound after the header-only return. The bound guards the roster's allocations alone. Past it, a header-only call was refused and `memory_total()` would panic in the compositor, the netstack and the file server. This was on main too. So "a header alone is never refused for memory" is now exact. - Since round 1's charge, the kernel answers a roster with nothing for two reasons: a capability without `ROSTER`, or a job without the room to hold the roster. `toyos_abi::syscall::sysinfo` folds both into 0. So `SysCap::roster`'s doc, toybox's roster helper and `ps`'s message name both causes, rather than blaming the capability for an exhaustion. Giving `roster` the refusal's kind would reach its nine callers, most of them guest tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator verification of round 3's fix at 90ef886: B1 resolved in text ( |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90ef886 to
64eb00e
Compare
The shared boots and the spawn A/B this change was to be judged by, named with what main must show, for the batched boot of main when a bench returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- osbench's IPC rendezvous, the console's surface port, the metal image's `power` connector and std's files module now say what each is: a port's own connector, one provided to the shell, one of the runner's services, and the read-only ancestors of the directory capabilities. - The supervisor's provided-connector comment drops the deleted design's reasoning: a row that does not receive the name gets nothing because its row decides its services. - std's `provide` says the refusal applies only to a child whose row receives the name. - A malformed table's length line names the length word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h refusals - `SYS_SYSINFO` checks its live-thread bound after the header-only return. The bound guards the roster's allocations alone. Past it, a header-only call was refused and `memory_total()` would panic in the compositor, the netstack and the file server. This was on main too. So "a header alone is never refused for memory" is now exact. - Since round 1's charge, the kernel answers a roster with nothing for two reasons: a capability without `ROSTER`, or a job without the room to hold the roster. `toyos_abi::syscall::sysinfo` folds both into 0. So `SysCap::roster`'s doc, toybox's roster helper and `ps`'s message name both causes, rather than blaming the capability for an exhaustion. Giving `roster` the refusal's kind would reach its nine callers, most of them guest tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1252 at 64eb00e (rebase onto main 8188095), round 4Scope: the rebase only. I ran Architectural assessmentThe rebase keeps both designs whole.
What I checked
FindingsB1 BLOCKER:
B2 BLOCKER: the description's "so every commit builds" is false.
S1 SUGGESTION:
S2 SUGGESTION: 150fcb8's message.
EvidenceAt 64eb00e, in a detached worktree with its own target directory. All commands exited 0:
Per commit, in a second worktree, for every commit that changes a Not built per commit: the ToyOS-only crates (supervisor, shell, guest tests and others), which need the guest sysroot. The cheapest check is Guest rows: 43112704482 (at 64eb00e) and the control 43102568233 were both still queued when I wrote this. There is nothing to read yet. SEND BACK |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64eb00e to
5dd83dc
Compare
The shared boots and the spawn A/B this change was to be judged by, named with what main must show, for the batched boot of main when a bench returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- osbench's IPC rendezvous, the console's surface port, the metal image's `power` connector and std's files module now say what each is: a port's own connector, one provided to the shell, one of the runner's services, and the read-only ancestors of the directory capabilities. - The supervisor's provided-connector comment drops the deleted design's reasoning: a row that does not receive the name gets nothing because its row decides its services. - std's `provide` says the refusal applies only to a child whose row receives the name. - A malformed table's length line names the length word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h refusals - `SYS_SYSINFO` checks its live-thread bound after the header-only return. The bound guards the roster's allocations alone. Past it, a header-only call was refused and `memory_total()` would panic in the compositor, the netstack and the file server. This was on main too. So "a header alone is never refused for memory" is now exact. - Since round 1's charge, the kernel answers a roster with nothing for two reasons: a capability without `ROSTER`, or a job without the room to hold the roster. `toyos_abi::syscall::sysinfo` folds both into 0. So `SysCap::roster`'s doc, toybox's roster helper and `ps`'s message name both causes, rather than blaming the capability for an exhaustion. Giving `roster` the refusal's kind would reach its nine callers, most of them guest tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator verification of the rebase round-4 fixes at 5dd83dc: the tree differs from 64eb00e only in comments ( |
Run 43102568233 ran kernel_objects_refused_alone and a_clean_stop_proves_its_image at main e10b6fd: both passed. The record names that result and what it leaves open: a single green run on main cannot tell an intermittent race there from a defect of #1252, so the record's case still rests on reading the test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`MAX_USER_STR` is gone; `MAX_HANDLES` stands where it stood. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared boots and the spawn A/B this change was to be judged by, named with what main must show, for the batched boot of main when a bench returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- osbench's IPC rendezvous, the console's surface port, the metal image's `power` connector and std's files module now say what each is: a port's own connector, one provided to the shell, one of the runner's services, and the read-only ancestors of the directory capabilities. - The supervisor's provided-connector comment drops the deleted design's reasoning: a row that does not receive the name gets nothing because its row decides its services. - std's `provide` says the refusal applies only to a child whose row receives the name. - A malformed table's length line names the length word. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 1 charged every answer to the caller's job for its copy-out, the 48-byte header alone included. Under memory exhaustion a header-only call, which nothing else refuses, was then answered `ResourceExhausted`, and `toyos::system::memory_total()`, which asserts a whole header, would panic in the compositor, netstack or a file server. Now only a roster is charged for the call; a header alone is a fixed few bytes and never refused for memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng its row Seen in guest run 43092179151: the Log server's restarts under memory exhaustion each panicked at fileserver/src/main.rs:290 with "its row is no pager row" for a ResourceExhausted, until the supervisor closed its ports for the boot. Main has the same code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ay still be written The race behind run 43092179151's red: a process that ends inside the test's window frees memory, and the test's refusals are judged against memory it did not take. The mechanism is inferred; the control on main is run 43102568233. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h refusals - `SYS_SYSINFO` checks its live-thread bound after the header-only return. The bound guards the roster's allocations alone. Past it, a header-only call was refused and `memory_total()` would panic in the compositor, the netstack and the file server. This was on main too. So "a header alone is never refused for memory" is now exact. - Since round 1's charge, the kernel answers a roster with nothing for two reasons: a capability without `ROSTER`, or a job without the room to hold the roster. `toyos_abi::syscall::sysinfo` folds both into 0. So `SysCap::roster`'s doc, toybox's roster helper and `ps`'s message name both causes, rather than blaming the capability for an exhaustion. Giving `roster` the refusal's kind would reach its nine callers, most of them guest tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 43102568233 ran kernel_objects_refused_alone and a_clean_stop_proves_its_image at main e10b6fd: both passed. The record names that result and what it leaves open: a single green run on main cannot tell an intermittent race there from a defect of #1252, so the record's case still rests on reading the test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `testcases` boot owed on metal for #1252 now names the four shared-boot members whose rebased forms have only been compiled: #1261's `poll_belongs_to_its_handle`, which connects through its connector where it opened a one-entry namespace, and `messages_are_whole`, `moved_rights` and `mutual_kill`. No guest row runs them until #1271's shared-boot row lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
258a5e9 to
19efa90
Compare
|
Orchestrator verification of the rebase onto d1cefba (#1242) at 19efa90. I checked the range-diff against 258a5e9, the head review r5 marked LAND:
The landing run 43146959560 at 19efa90 passed all 10 rows ( |
… it out #1252 put the child's names for its endowments into the startup block, after the arguments and environment, in the same 2 MiB less a page. A caller's room for its own arguments and environment then depended on how many endowments the spawn carried and how long their names were, which nothing bounded but the block, so no constant could say what a caller may pass: MAX_ARGS answered as if no names were there. A names table is now at most startup::MAX_NAMES bytes, 64 KiB, refused past it as a spawn too big. That share of the block is many times any table a spawn in this tree makes: as many names as a spawn endows, of about 680 bytes each. MAX_ARGS leaves it out, so a caller's MAX_ARGS bytes fit whatever the spawn endows. _SC_ARG_MAX is 2023395. The MAX_ARGS test now spawns with one name filling a table's most bytes, and startup's test holds a table of exactly MAX_NAMES bytes and refuses one byte more. 206_libc_refusals.expect and the owed-on-metal record say 2023395. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rights `HandleTable::get_sent`, the lookup of a handle a peer sent (`SYS_CONNECT`'s connector, `SYS_SPAWN`'s place and job), checked the handle's rights before its kind, so a handle of another kind that also lacked the right answered `PermissionDenied`. Its own contract, `connect`'s and the spawn fields' ABI docs all answer such a handle `InvalidArgument`, and a right means something only on its own kind. `connect` through an acceptor, which carries no `WRITE`, is the case `abuse_listener_hijack` holds, and it went red in `testcases_members_exit_0` once #1252 made connecting need `WRITE`. The kind is now checked first. A place or a job of another kind without `WRITE` answers `InvalidArgument` too, as their docs already say. `WrongType`'s doc says `get_sent` never builds it, and `process_tree`'s `a_pipe_is_no_place` no longer explains its refusal by the old order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The adopter, which std's `adopt_services` gives `fs:/system`, then checked that `toyos::endow::services()` listed it. std links its own copy of `toyos`, and each copy keeps its own adopted services: std's adoption filled std's copy, and the program's listing read its own, which adopted nothing. So the check went red in `testcases_members_exit_0` (run 43156966054, `assert_failed::<Vec<&str>, [&str; 1]>`). - The adopter still checks, through its own copy, that it was endowed no service: that copy never adopts, so the check holds at any point. The check after the adoption goes. The helper's spawn proves the adoption, since std reads the helper's image through `/system`, which std holds only by adopting it. - std's `adopt_services` says what ends its adoption: std's own resolution of a file or lookup of a service. - `issues/a-programs-own-toyos-never-sees-the-services-std-adopted.md` records the two adopted sets, the defect #1226 closed for stdio's state. - The owed-on-metal record for #1252 says `testcases_members_exit_0` runs the members #1252 adapted on QEMU. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d nothing asked costs one branch - The Trust page names SSBD among what a program that asks gets, and says any program may ask for a child it starts (B2). - in_force's doc says what it holds in force by construction (B3, in the commit before this one, rebased). - The per-CPU flag whether IA32_SPEC_CTRL exists is the test kernel's alone, for its debug read (B4). - A CPU whose decision names nothing on request returns from switch_to after one load (S1). install(), called beside report(), gives each CPU its switch state, and the base it installs is the IA32_SPEC_CTRL its bring-up read back rather than a second decision (S2, S7). LastAsked names a pid (S3). - speculationprobe refuses an error answer (S4). speculation_on_request says it holds nothing where CPUID enumerates no IBPB, as TCG's qemu64 does not, and states its targets at its registration (S5); back on the shared CPU after the probe it reads the asked bits clear (S6). Rebased onto main 302fb36: #1252's spawn path takes the job and no labels, and the ask rides PendingHandles beside the pager as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… it out #1252 put the child's names for its endowments into the startup block, after the arguments and environment, in the same 2 MiB less a page. A caller's room for its own arguments and environment then depended on how many endowments the spawn carried and how long their names were, which nothing bounded but the block, so no constant could say what a caller may pass: MAX_ARGS answered as if no names were there. A names table is now at most startup::MAX_NAMES bytes, 64 KiB, refused past it as a spawn too big. That share of the block is many times any table a spawn in this tree makes: as many names as a spawn endows, of about 680 bytes each. MAX_ARGS leaves it out, so a caller's MAX_ARGS bytes fit whatever the spawn endows. _SC_ARG_MAX is 2023395. The MAX_ARGS test now spawns with one name filling a table's most bytes, and startup's test holds a table of exactly MAX_NAMES bytes and refuses one byte more. 206_libc_refusals.expect and the owed-on-metal record say 2023395. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d nothing asked costs one branch - The Trust page names SSBD among what a program that asks gets, and says any program may ask for a child it starts (B2). - in_force's doc says what it holds in force by construction (B3, in the commit before this one, rebased). - The per-CPU flag whether IA32_SPEC_CTRL exists is the test kernel's alone, for its debug read (B4). - A CPU whose decision names nothing on request returns from switch_to after one load (S1). install(), called beside report(), gives each CPU its switch state, and the base it installs is the IA32_SPEC_CTRL its bring-up read back rather than a second decision (S2, S7). LastAsked names a pid (S3). - speculationprobe refuses an error answer (S4). speculation_on_request says it holds nothing where CPUID enumerates no IBPB, as TCG's qemu64 does not, and states its targets at its registration (S5); back on the shared CPU after the probe it reads the asked bits clear (S6). Rebased onto main 302fb36: #1252's spawn path takes the job and no labels, and the ask rides PendingHandles beside the pager as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
The kernel kept, for every process, the labels its parent put on each endowment: it compared three of them (
job,self,quit), answered them back throughSYS_ENDOWMENTS, charged every process for 32 entries and 4 KiB of label text, and held a namespace object mapping service names to connectors behind three more calls (SYS_NAMESPACE_BUILD,SYS_NAMESPACE_OPEN,SYS_NAMESPACE_NAMES). None of that is the kernel's: it is what one program calls a handle it gave another.Resulting behavior
RawHandle::SELF,RawHandle::NOTICE, then the endowments in the order the spawn listed them (RawHandle::endowed(i)). The kernel installs them so and asserts the order.bytes u32 | count u32 | count x {len u16 | name}) whose entryinamesendowed(i). The child's SDK checks it once, whole, before any lookup. A table that is not a list of distinct, non-empty UTF-8 names ends the child with status 125 and a line on thefd:2it still names. The relocator's pre-relocationfd:2lookup walks the same table with the stack alone.SpawnArgs::jobreplaces thejoblabel. It is a borrowed handle carryingWRITE, resolved once likeplaceandpager. So the commit's re-check that a moved handle still named the job goes.SYS_CONNECT(102) connects through a connector carryingWRITE. Connectors are now mintedDUP | TRANSFER | WRITE. One withoutWRITEcan only be passed on.SYS_ENDOWMENTS(99),SYS_NAMESPACE_BUILD(101) andSYS_NAMESPACE_NAMES(170) are gone, and the freed numbers stay unassigned.ObjectKind::Namespacegoes and its word 8 is retired: a kind's word is ABI and never reassigned, asObjectKindstates since A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239. A const check holds word 8 unused, and every other kind keeps its word. Also deleted: the kernel's namespace object,SpawnEndow,EndowEntry, the label blob and its per-process charge,MAX_NAMESPACE_ENTRIESandMAX_SERVICE_NAME.SYS_NAMESPACE_OPEN's name was the last caller of the kernel's 64 KiBuser_str, since A spawn's path is one line, and no judge finds a child by it #1238 bounds a spawn's path byMAX_PATH. Souser_strandMAX_USER_STRgo, and each thread is charged for what a spawn copies in and holds where it may be preempted (its path, name and build-id at their bounds, its ranges, its endowments and the mappings of the device claims it moves) instead of two 64 KiB strings and a page.SYS_SYSINFO's roster, which it holds across a copy-out that can wait, is charged to the caller's job for the call, and the live-thread bound guards the roster alone: a header alone is never refused.MAX_SPAWN_ENDOWMENTSis 96: the 30 one spawn carried plus the 64 a namespace held.toyos_abi::boot::supervisorstates.svc:<name>(a row'sreceives, its view'sfs:directories, minted grants) orprovide:<name>(one a launching client sent).toyos::endow::servicelooks one up and connects. A helper is given a duplicate of each service its parent holds, narrowed byCommandExt::services_only. Everything else is taken once by name. A provided connector reaches only a row that receives its name, as the client's handle narrowed toDUP | TRANSFER | WRITE. A handle without those rights refuses the launch, and one the kernel does not report as a connector is refused by name (A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239's typed receive). A connector a peer sends, such as a session's launcher and controls, is typed only where the kernel reported a connector. Swap's connector arrives withWRITEalone. The supervisor adopts its own directory capabilities (std::os::toyos::fs::adopt_services), and no child inherits them. So a storage row, whose view is empty, now starts holding no file grant, where it used to inherit the supervisor's whole tree. That closesissues/storage-rows-start-holding-the-supervisors-whole-tree.md, and the new guest testadopted_services_stayholds it.-cinterrupt race, fixed. A quit of a-cshell reaches its children by the kernel's walk. A child that lands after the quit holds the reason unread, or none, and that quit never asks it. The shell meant to ask such a child itself, but it looked at a flag its listener thread set after reading the notice outside any lock.cat) then ran for ever, and the shell with it. That is thetty_interruptstall in guest run 43053472977, and main has the same race.-cshell is marked as one before it listens.system/shell/src/quit.rs, which names nothing of the SDK. The new host crateshell-copiescompiles it as it is and runs one interrupt and one child through every order.issues/a-c-shell-cannot-tell-a-child-its-quit-asked-from-one-that-landed-after.md, with the kernel-side exit.Kernel: 56,188 lines in 212 files at main d1cefba to 55,842 in 211 (
git ls-treeofkernel/src/**.rs). The whole diff against d1cefba is 177 files, +2,470/-2,618.Decisions (the design the orchestrator confirmed; recorded in the manual's authority page)
WRITEon the connector. A provided connector goes only to a row that receives its name. That second half is the orchestrator's ruling after review round 1, and it reverses the earlier transfer-only forwarding: rights only narrow, so a forward withoutWRITEhad no consumer. Since A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239, the supervisor refuses at the launch, by name, a provided handle the kernel does not report as a connector.svc:andprovide:stay two inherited classes, with one carry. The shell forwardsprovide:connectors without knowing their names.toyos::spawn::inherited).services_onlynarrows it. Stage 2 of the isolation track tightens it later.MAX_SPAWN_ENDOWMENTS = 96. What bounds the names is stated at the constant: the startup block.SYS_CONNECTtakesSYS_NAMESPACE_OPEN's number, and the freed numbers stay unassigned.ObjectKind's words are never reassigned: the namespace's 8 stays retired.Checks and evidence
Evidence branch
pr-1252, at3a701aae4. Each log ends with its exit code; local paths are masked. The head is 19efa90, rebased onto main d1cefba.rebase-d1cefbaf9/holds thegit range-diffof this history against 258a5e9's, the head reviewed LAND; only the first commit differs.The rebase onto d1cefba brought #1242, under which every server admits its clients through
toyos::admission, with #1251, #1257, #1262 and #1273. Three files conflicted, all in the first commit:system/supervisor/src/main.rs: the imports alone. The launcher, grants and admin ports admit throughtoyos::admission(main), and a launch's batch reaches the same typed extras, job and place, narrowed toPROVIDEDand endowed only to receiving rows (this change). The first resolution kept main'sEndowmentsimport, which this change no longer uses; the image build refused it, and the first commit drops it.system/usbd/src/main.rs: the imports. usbd'sinspectport admits throughtoyos::admission(main), andDEV_PREFIXcomes fromtoyos::endow(this change).issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md: main's rewritten account, which cites the supervisor'sbuild_namespace, now citesreceived, its counterpart here.No
take_handlescaller this change has needs more:lib/session's two connectors and the supervisor's launch batch are typed as before, and #1242 adds no namespace use. The first commit's message gives the kernel's size against d1cefba (55,830 lines in 211 files at that commit).The rebase onto a7ca8ad brought #1261, under which a poll belongs to the handle it was made through. Main moved there while the rebase onto 429f772 was being checked:
kernel/src/object/ops.rsconflicted, in the first commit. A poll belongs to the handle it was made through, and a ring to its one submitter #1261 deletesclose_ends_polls, and this change's edit to it only removed the namespace's arm, so the edit goes with the function.poll_belongs_to_its_handleopened a one-entry namespace. In the first commit it connects through its connector.adopted_services_staycalledadopt_services, which the A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239 resolution madeunsafe, outsideunsafe. No host check and no image build compiles either binary. The first commit fixes both.The rebase onto 429f772 brought #1239 under this change, with #1267, #1263, #1223 and #1237. Under #1239, a receive reports each handle's kind,
ReceivedHandle::typed::<T>()is the one way to type a handle a peer sent, andFromHandle::from_handleisunsafewith the giver's word as its contract. Eleven files conflicted with main, ten of them in the first commit, and each resolution keeps both sides:issues/a-received-handle-has-no-knowable-type.md(deleted on main, edited here): deleted. A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239 closed it, and this change's edit only updated a fact about the namespace calls.sdk/toyos/src/namespace.rs(deleted here, edited on main): deleted with the namespace. Main's edit removedNamespace::received, the unchecked constructor of a namespace a peer sent. The behaviour it carried is kept for the connectors that replace namespaces. A peer-sent handle is typed only where the kernel reported its kind, so this change's ownConnector::receivedis deleted too.lib/session/src/lib.rs: a session's launcher and controls, which the supervisor sends, are typed throughtyped::<Connector>()where main typed two namespaces. They are refused by name when they are not connectors.sdk/toyos/src/endow.rs: main'sFromHandle,OfKind,ClaimTypeand thefrom_handle!list with kinds, withoutNamespace. This change's name table takes an endowment through the unsafefrom_handleon its parent's word, with main's justification.sdk/toyos/src/quit.rs: a process starts holding its notice atRawHandle::NOTICE(this change), so main'sfrom_handle!forNoticeis not kept: nothing takes a notice by type.sdk/std/os/fs.rs: main madeadopt_namespaceunsafebecause the handle's kind is its caller's word. It goes with the namespace, andadopt_services, which replaces it, isunsafefor the same reason.system/supervisor/src/main.rs: a launch's extras, job and place are typed as connectors, a job and a process (main), and refused by name otherwise. The extras are then narrowed toPROVIDEDand endowed only to receiving rows (this change). The supervisor's own services go toadopt_servicesunder a SAFETY comment, and so does its system capability at its boot handle.tests/guest/rust-tests/src/bin/{moved_rights,mutual_kill,process_tree}.rs: imports only. Main'sReceivedEntryandtyped, and this change's connector API.sdk/abi/src/handle.rsstopped the rebase at the commit that retired word 8. Main'sObjectKindnow states the rule itself, so the first commit retires word 8 directly under one rule text, with the const check,from_word's lookup and the census's index. No commit now renumbers kinds that a receive reports, and the separate commit is gone. A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239's testa_received_handle_is_typed_only_as_the_kind_the_kernel_reportedtookObjectKind::ALL.len(), 19, as a word no kind has, which isDeviceSet's now that word 8 is retired. The test takes word 8 and the word past the last kind instead, in the first commit. The host checks at 43f760fad found it (r11/,test-sdk), andr11b/passed with the fix.system/supervisor/src/main.rsstopped it again at the commit that narrows provided connectors: the same typed launch handles, with its rights rule.sdk/std/os/fs.rsandsystem/diskserver/src/session.rsstopped it at the commit that had moved the last uses ofConnector::receivedtoFromHandle. That conflict is with the first commit, which now does the same, so both keep the first commit's form.diskserver::heldtypes a duplicate of its own endowed connector on its parent's word.Two commit messages are corrected for this: the provided-connector commit no longer says a connector's kind is checked at the program's connect, and the malformed-table commit is retitled now that it no longer touches
received. The first commit's message also states word 8's retirement and the typed connectors, and gave the kernel's size against 429f772.The rebase onto 75bdec3 brought #1259 under this change. With it, a memory object's range is mapped where the caller says,
SYS_SHM_MAPbecomesSYS_VM_MAP, and a spawn's range may share its object's pages. #1243, #1266 and #1232 came with it. Four files conflicted, all in the first commit, and each resolution keeps both sides:kernel/src/syscall/dispatch.rs: the import list has neither main's deletedsys_shm_mapnor this change's deleted namespace calls, and addssys_connect. Main'sSYS_VM_MAParm merged unchanged.docs/src/using/interface.md: main's account of a child's ranges (a memory object's pages or a private copy of them), followed by this change's names in the startup block.tests/guest/rust-tests/src/image.rs: this change'slaid_namingnow lays out through main'sImage::source().tests/guest/rust-tests/src/bin/spawn_image_object.rs: the block takes this change's empty list of names, and the layout takes main'sSource::Copied.Nothing that merged cleanly needed adapting:
loader::Mapby its type, so A memory object's range is mapped where the caller says, and an immutable one is shared and executable: /system text is ROOT's own pages #1259's larger range is charged as it is, and A memory object's range is mapped where the caller says, and an immutable one is shared and executable: /system text is ROOT's own pages #1259 adds no other allocation to the spawn's path.Review round 4 rewrote the history so that each commit checks the x86-64 kernel. Until now, the 9 commits from the first through the shell's issue record left
user_strdead, which that target's-Dwarningsrefuses.user_str,MAX_USER_STR, their import and their mention inkernel/pure/sched/cpu.rsleave in the first commit, together with the per-thread charge that replaced them. The commit that deleted them later is gone. The first commit's message now describes both, and the two adaptations folded into it, and gives the kernel's size at that commit.Handles::table's doc loses the stdio clause the rebase brought back, andsystem/diskserver/src/session.rssays a session reaches its service through the same connector, not the same name.cargo check -p kernel --target x86_64-unknown-nonepasses (exit 0) at each of the 22 commits from 8188095 to 5dd83dc (kcheck-range/, with the rewrite's edits infold_edits.py). It passed again at each of the 23 commits on 75bdec3 (kcheck-range2/). It passed, together withcargo check -p toyos --all-targets, at each of the 22 commits from a7ca8ad to c37b68a61 (kcheck-range5/). Now both pass at each of the 23 commits from d1cefba to e4c6da589 (kcheck-range6/), which differ from 19efa90's range only in the supervisor's import line, which neither check builds. The control, the old first commit 150fcb8, fails (exit 101) on the deaduser_strin every run. Only those checks ran at each commit; the other build shapes ran at the head.The earlier rebase, onto 8188095, brought #1246 (a thread carries its process's handle table,
with_handles), #1235's block-session silence bound, #1226, #1216, #1260 and others under this change. Five files conflicted, and each resolution keeps both sides:kernel/src/loader/mod.rs: main wraps the child's table inprocess::Handles::new(handles, pager); this change's commit answers(table, held)with noEndowments.kernel/src/loader/start.rs: the spawn's commit keeps this change's logic (no labels, no job re-check, the kernel's two handles first,handedsized once) under main's lock on the caller's table (handles()), notProcessData's.kernel/src/process.rs:ProcessData::handlesstays main'sArc<Handles>, and this change'sEndowmentsstays deleted.kernel/src/syscall/ipc.rs: main's namespace syscalls, moved ontowith_handles, stay deleted;sys_connectlooks its connector up throughwith_handles.system/diskserver/src/session.rs: main'sClientEndsand a fresh region per reconnect, with this change's handshake through a connector.Two adaptations that did not conflict are folded into the first commit:
spawn_joblooks a job up throughwith_handles.History before the rebase, carried over unchanged:
SYS_SYSINFOcharge also charged a header-only call, somemory_total()could panic under exhaustion. Now only a roster is charged.SYS_SYSINFOchecks its thread bound after the header-only return, andpsnames both causes of a refused roster.Host checks at e4c6da589, a clean tree (
r13/). 19efa90 differs from it only in the supervisor's import line, in every commit. No host suite or clippy shape builds the supervisor, so these hold for 19efa90:toyos-abi,toyos, the kernel library withsched-check,toyos-manifest,toyos-session,toyos-swap,shell-copies,toybox-copies, and disks, diskserver, fileserver, netstack, nvme, pkg, session, soundserver and sshserver;build/clippy.rs: host, host with the kernel's controls, x86_64 default, both actuator sets, the instrument set, the two heap-band sets, aarch64 default, both actuator sets and its instrument set;toyos(all targets andfd-table), the libraries,shell-copies,toybox-copiesandtoyos-abi.All exit 0. The image (
--build-only) exited 101 at e4c6da589 on the unused import, and exits 0 at 19efa90 (r13b/).Guest crates compiled at 19efa90 against its sysroot (
r13-guest/):rust-tests,test-runner,osbench,metalprobe,hangupholder,inputsink,pciholderandstarvelingexit 0.kernelproberefuses every target but AArch64, by design, and is not compiled here.pagerprobeconvertstoyos::OwnedHandleinto std's without declaring the SDK'sstdfeature, which provides that conversion. This change touches neither, and the harness's build of thetestcasesimage, which carries it, has built it.Host (all at 041f1b8)
cargo run -q -- --build-onlyexits 0:build/build-only-041f1b81e.log.host-r6/. They are-p toyos-abi,-p toyos,-p kernel --lib --features sched-check,-p toyos-manifest -p toyos-session -p toyos-swapand-p shell-copies, plus disks, diskserver, fileserver, netstack, nvme, pkg, session, soundserver and sshserver by manifest. The full--ci hostis not run, and the merge queue runs it.-D warnings, exits 0:clippy-r6/. That covers the kernel's host tests, x86_64 with and without actuators, aarch64,toyoswith all targets and withfd-table, the three libraries,toyos-abi, andshell-copieswith all targets. The host-buildable programs listed above were tested, not linted, locally.The shell's race (unchanged since round 0)
cargo test -p shell-copies:shell-race/.mut-landed.patchreds[Quit, Land, Look, Heard](exit 101, restored), and the baseline exits 0.Guest rows, now the only integration evidence
a_launch_gives_each_unsent_stdio_slot_a_null,session_lifecycle,libc_children,kernel_objects_refused_aloneanda_clean_stop_proves_its_image, each one ofMACHINE_TESTS;virt_user_mode,virt_user_copy_faults,virt_first_entry,virt_jobs_at_el2andvirt_smp, each one ofSCREEN_TESTS.kernel_objects_refused_alone,a_clean_stop_proves_its_imageandsession_lifecycle, all 3 pass (verdicts).kernel_objects_refused_alone: a pipe's first write succeeded while the test held every frame. While it held them, the Log file server (pid 9) ended, freeing its memory. Each restart then panicked creating its pager (ResourceExhausted,fileserver/src/main.rs:290).a_clean_stop_proves_its_image: the 16550 file that the harness read as the first loader pass held no loader line.session_lifecycle,a_launch_gives_each_unsent_stdio_slot_a_null,a_row_reaches_its_own_state_and_no_other,osbench_capability_groups_exit_0,a_spawn_from_an_image_frees_it_when_the_child_ends,a_user_copy_faults_in_mid_copy_or_ends_as_bad_address,libc_children,netstack_holders_exported_to_children,usbd_drives_the_spare,kernel_objects_refused_alone,memory_floor_holds_the_controls,a_window_is_drawn_at_the_scale_and_relaid_at_the_next,the_data_partition_beside_the_stick_is_designated,a_clean_stop_proves_its_imageandnvme_disk_boots;virt_user_mode,virt_user_copy_faults,virt_first_entry,virt_jobs_at_el2andvirt_smp.a_launch_gives_each_unsent_stdio_slot_a_null,session_lifecycleandlibc_children, all 3 pass (verdicts).Owed on metal: the benches are down, and metal is no longer a landing condition. The record is
issues/the-kernel-is-small-interrupts-post-and-threads-wait.md; the orchestrator runs these on main when a bench returns.endow-aboots (testcases,shared-debugandproctreecase) on the T14 and the AMD bench. Main must show:endowment_denied,abuse_handle_table(97 endowments refused, 96 start),abuse_connect_flood(a refused connect leaves the caller's table as it was),abuse_listener_hijack,user_copy_faults,adopted_services_stay,handle_lifetime,process_treeandlaunch_toctou;testcasesmembers this PR adapted, which have only been compiled in that form, passing:poll_belongs_to_its_handle(A poll belongs to the handle it was made through, and a ring to its one submitter #1261's, which connects through its connector where it opened a one-entry namespace),messages_are_whole,moved_rightsandmutual_kill. No guest row has run them: The shared boots run as guest rows, each member judged by its own exit #1271's shared-boot row, on main since 3c5c555, will. 19efa90 names them in the record;spawn_costwithin its guard.spawn_costand osbench's spawn rows (system's process spawn, theprocsgroup). This change's effect on spawn cost is not yet measured on metal.sys_connectnegative control (aRights::NONEmutant image,mut-connect.patch). It is a control image rather than a row of main. The other kernel control the design named, endowments installed before the kernel's own two, is anassert_eq!on every install, so a boot fails on it at once.Risks and open items
issues/a-file-server-whose-pager-is-refused-for-memory-panics-blaming-its-row.md: the Log server's restarts under exhaustion panic atfileserver/src/main.rs:290with a false message, until the supervisor closes its ports for the boot.issues/kernel-objects-refused-takes-every-frame-while-its-own-line-may-still-be-written.md: the test's window race. Control 43102568233 at main passed both rows. One green run cannot tell an intermittent race on main from a defect of this PR, so the record's case still rests on reading the test, and 135fcb4 says so in the record.🤖 Generated with Claude Code