Repository navigation
A child's arguments and environment are memory its parent lays out - #1200
Conversation
kernel/src holds 215 files and 55,245 lines, from 215 and 55,386. The startup block's reservation is the module's own constant. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Review r1 of #1200 at cf06504 (merge base f9b5d7f)Read: both commits, the whole diff, every changed file, the kernel's spawn path and crash-frame naming, the SDK's spawn, std's ArchitectureThe ownership is right. What a parent tells its child is the parent's data and the child's to read, and the kernel loses a copy, a store, a syscall and a write path for it. The convention lives in the SDK ( Verified as claimed: What was askedDoes the child validate the block? No, by decision 6, and I accept it. A block is malformed only if the spawner bypassed Can the kernel still trust it? No. Nothing in the kernel reads the block or any argument. The only userland text a spawn hands it is the path, bounded and checked as before, and the endowment labels. Does the fixed address weaken anything? Not today, since the image, the stack and every mapping are already at constant addresses. It does add debt against a recorded defect: see blocker 2. The cap. Refused before anything is created, by name ( Every spawner. std: environment captured with Deletion. Complete. No reference to Decision: the name from the path. Accept. I traced where it differs and the PR's account is right: only a launch that reaches a row through a link. A helper keeps the link's own name, because std passes the path it resolved, so the shell's Decision: fixed address versus an opaque word. Accept the address. The word would cost a The manual. Accurate and names no code. Evidence. Verified: both guest runs are of 494063c and green, 6 of 6 and 4 of 4. The host run's one red step is the recorded macOS test, in a file this branch does not touch; the other BLOCKER
SUGGESTION
For the orchestratorThe PR body's "tests +238 -297" does not add up against the 545 and 513 total; I count +210 -191. SEND BACK |
startup_block's children exit with a code of their own once their checks hold, and the parent asserts each: a child told to read 300 KiB that found nothing took the other child's role and exited 0, which was accepted. issues/no-user-address-is-drawn-per-spawn.md records the startup block as a fourth constant address, the one whose bytes another party chooses, with its exit: it moves with the image. toyos::startup has one constructor, from arguments and environment, so no caller can hand Program::starting_with a block past its reservation. The track says the kernel keeps two userland texts, the endowment labels being the other. abuse_spawn_maps moves a program's range past the user half, not the block's page. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… run Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
… unchanged soundserver's bell arrives READ|WAIT, so the wait side really does go when its client's table does, as the comment beside it claimed. `RIGHTS_UNCHANGED` is `TransferEntry::UNCHANGED`: `SYS_HANDLE_DUP`'s word for the source's own set is u32::MAX where it was u64::MAX, and its two SDK wrappers are the only writers. The manual says a holder keeps a handle until it gives it up or ends. issues/a-received-handle-has-no-knowable-type.md records that it is taken up as (handle, ObjectKind) at receive, the three constraints its review set, and that event sides widen its reach. The spawn-side fixes of this round follow in the rebase onto #1200. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Review r2 of #1200 at 52ec105 (code at e9bbb29), changes since cf06504Read: the four commits since round 1, their whole diff, each changed file, the kernel's spawn arm and every reader of a process's name, the SDK's spawn, the supervisor's launcher, the roster's and the inventory's userland readers, ArchitectureThe name field is sound and its condition holds: the name is a label and nothing decides by it. Against main it adds no authority, since main's kernel already took the name from the parent's Who reads the name. Written once, at The boundary. Complete. Is the ABI shape minimal. Yes. One pointer and length, one bound, one validator both sides call. An inline 28-byte field would save the second copy and cost a rule for the bytes after the NUL: a wash, and the pair matches the struct's other texts. No field at all means the child naming itself at entry, which leaves a process that dies before its runtime runs without a name in its own exit record. Round 1. Both blockers are resolved. A The two repaired members. Correct, and the account is right: at cf06504 the helper made the block's object inside a spawn from a full table and panicked. Both lay out before filling. The kernel claim each holds is unchanged.
BLOCKER
SUGGESTION
Landing precondition, not reviewedThe three staged boots of e9bbb29 on the T14, judged by the harness over the readbacks at exit 0 with ten passed. A boot's own
Fixing the two blockers changes no image, so the staged images stay valid unless the rename is taken. For the orchestrator
SEND BACK |
issues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.md
is closed. Its forged name, `test_rs_job pid=1 code=0`, is refused at spawn:
a name is 1 to 27 bytes of UTF-8 with no control character and no white
space (toyos_abi::syscall::process_name), so it cannot carry a record's
separators. The claim is held by sdk/abi's
`a_process_name_is_one_bounded_token` on the host and by
abuse_spawn_path_and_name's two-word and two-line names on the T14's boot.
`make_name`, which the record cited, went with the name field.
The same forgery through the path is not closed and has its own record,
issues/a-spawns-record-quotes-the-path-its-caller-states.md: `spawn: {path}
pid=` writes a label its caller states, unchecked and unescaped. Read, not
run.
build/bootlog.rs and tests/common/metal.rs say where a recorded name comes
from now, the parent's SDK, and what `recorded_name` must agree with.
abuse_spawn_labels is abuse_spawn_path_and_name, since "labels" is the
endowment blob in this ABI. The SDK's `name` is private. The kernel spells a
name's array one way, THREAD_NAME_LEN, which is the roster's NAME_BYTES, and
`name_str` no longer answers `?` for a name that cannot be.
The stall's sighting says the run's serial artifact ends at the console's
hand-over and holds no exit record of the nested shell.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Review r3 of #1200 at 9885370, changes since e9bbb29Read: both commits in the range, their whole diff, every writer and reader of a process's name, the path's way from The change listComplete as stated. The range holds the five code items, comments in
|
…'s record says its worst case build/bootlog.rs held kernel/src/process.rs to the text `THREAD_NAME_LEN: usize = 28`, which 9885370 changed, so `the_kernel_writes_the_records_the_host_reads` was red on every host from that commit. NAME_LEN is toyos_abi::inventory::NAME_BYTES, the constant the kernel's is defined as, and the needle goes; the test's doc no longer says the kernel truncates a name or that nothing links the crates. issues/a-spawns-record-quotes-the-path-its-caller-states.md states the line break: a path carrying a newline and a whole kernel record puts a line in /log that a judge reads as a verdict, from a spawn that need not succeed. Its exit is the orchestrator's decision in two parts, one for the forged line and one for the stated path. The stall's sighting gives the three UART logs' ends as they are. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
166944c to
ebf9269
Compare
kernel/src holds 215 files and 55,245 lines, from 215 and 55,386. The startup block's reservation is the module's own constant. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
startup_block's children exit with a code of their own once their checks hold, and the parent asserts each: a child told to read 300 KiB that found nothing took the other child's role and exited 0, which was accepted. issues/no-user-address-is-drawn-per-spawn.md records the startup block as a fourth constant address, the one whose bytes another party chooses, with its exit: it moves with the image. toyos::startup has one constructor, from arguments and environment, so no caller can hand Program::starting_with a block past its reservation. The track says the kernel keeps two userland texts, the endowment labels being the other. abuse_spawn_maps moves a program's range past the user half, not the block's page. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
issues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.md
is closed. Its forged name, `test_rs_job pid=1 code=0`, is refused at spawn:
a name is 1 to 27 bytes of UTF-8 with no control character and no white
space (toyos_abi::syscall::process_name), so it cannot carry a record's
separators. The claim is held by sdk/abi's
`a_process_name_is_one_bounded_token` on the host and by
abuse_spawn_path_and_name's two-word and two-line names on the T14's boot.
`make_name`, which the record cited, went with the name field.
The same forgery through the path is not closed and has its own record,
issues/a-spawns-record-quotes-the-path-its-caller-states.md: `spawn: {path}
pid=` writes a label its caller states, unchecked and unescaped. Read, not
run.
build/bootlog.rs and tests/common/metal.rs say where a recorded name comes
from now, the parent's SDK, and what `recorded_name` must agree with.
abuse_spawn_labels is abuse_spawn_path_and_name, since "labels" is the
endowment blob in this ABI. The SDK's `name` is private. The kernel spells a
name's array one way, THREAD_NAME_LEN, which is the roster's NAME_BYTES, and
`name_str` no longer answers `?` for a name that cannot be.
The stall's sighting says the run's serial artifact ends at the console's
hand-over and holds no exit record of the nested shell.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…'s record says its worst case build/bootlog.rs held kernel/src/process.rs to the text `THREAD_NAME_LEN: usize = 28`, which 9885370 changed, so `the_kernel_writes_the_records_the_host_reads` was red on every host from that commit. NAME_LEN is toyos_abi::inventory::NAME_BYTES, the constant the kernel's is defined as, and the needle goes; the test's doc no longer says the kernel truncates a name or that nothing links the crates. issues/a-spawns-record-quotes-the-path-its-caller-states.md states the line break: a path carrying a newline and a whole kernel record puts a line in /log that a judge reads as a verdict, from a spawn that need not succeed. Its exit is the orchestrator's decision in two parts, one for the forged line and one for the stated path. The stall's sighting gives the three UART logs' ends as they are. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…hat #1200 took issues/process-trees-metal-judge-waits-for-a-kernel-line-no-spawn-prints.md is closed by its exit's second way: the judge's count is gone (`process_tree` in tests/toyos.rs), and the T14's `proctreecase` boot of 9885370 is judged green by it, `process_tree` exiting 0. What the count meant to hold, a spawn refused after its admission giving back what admission took, is held on the host by kernel/proclife's tree tests, and on the T14 by abuse_spawn_maps, whose refusals come after admission and whose honest spawn runs after every one. The job keeps its own assertion that 64 zero bytes start nothing. The sighting of tty_interrupt's stall this branch added is dropped: main closed that record (ecb718f). The kernel's inventory, which main wrote while this was in review, no longer lists a child's arguments and environment as still held, and its ranking and sums lose that row. kernel/src is 215 files and 55,239 lines on this tree, from main's 55,379. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Review r4 of #1200 at
|
ebf9269 to
6f78518
Compare
kernel/src holds 215 files and 55,245 lines, from 215 and 55,386. The startup block's reservation is the module's own constant. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
startup_block's children exit with a code of their own once their checks hold, and the parent asserts each: a child told to read 300 KiB that found nothing took the other child's role and exited 0, which was accepted. issues/no-user-address-is-drawn-per-spawn.md records the startup block as a fourth constant address, the one whose bytes another party chooses, with its exit: it moves with the image. toyos::startup has one constructor, from arguments and environment, so no caller can hand Program::starting_with a block past its reservation. The track says the kernel keeps two userland texts, the endowment labels being the other. abuse_spawn_maps moves a program's range past the user half, not the block's page. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
issues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.md
is closed. Its forged name, `test_rs_job pid=1 code=0`, is refused at spawn:
a name is 1 to 27 bytes of UTF-8 with no control character and no white
space (toyos_abi::syscall::process_name), so it cannot carry a record's
separators. The claim is held by sdk/abi's
`a_process_name_is_one_bounded_token` on the host and by
abuse_spawn_path_and_name's two-word and two-line names on the T14's boot.
`make_name`, which the record cited, went with the name field.
The same forgery through the path is not closed and has its own record,
issues/a-spawns-record-quotes-the-path-its-caller-states.md: `spawn: {path}
pid=` writes a label its caller states, unchecked and unescaped. Read, not
run.
build/bootlog.rs and tests/common/metal.rs say where a recorded name comes
from now, the parent's SDK, and what `recorded_name` must agree with.
abuse_spawn_labels is abuse_spawn_path_and_name, since "labels" is the
endowment blob in this ABI. The SDK's `name` is private. The kernel spells a
name's array one way, THREAD_NAME_LEN, which is the roster's NAME_BYTES, and
`name_str` no longer answers `?` for a name that cannot be.
The stall's sighting says the run's serial artifact ends at the console's
hand-over and holds no exit record of the nested shell.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…'s record says its worst case build/bootlog.rs held kernel/src/process.rs to the text `THREAD_NAME_LEN: usize = 28`, which 9885370 changed, so `the_kernel_writes_the_records_the_host_reads` was red on every host from that commit. NAME_LEN is toyos_abi::inventory::NAME_BYTES, the constant the kernel's is defined as, and the needle goes; the test's doc no longer says the kernel truncates a name or that nothing links the crates. issues/a-spawns-record-quotes-the-path-its-caller-states.md states the line break: a path carrying a newline and a whole kernel record puts a line in /log that a judge reads as a verdict, from a spawn that need not succeed. Its exit is the orchestrator's decision in two parts, one for the forged line and one for the stated path. The stall's sighting gives the three UART logs' ends as they are. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…hat #1200 took issues/process-trees-metal-judge-waits-for-a-kernel-line-no-spawn-prints.md is closed by its exit's second way: the judge's count is gone (`process_tree` in tests/toyos.rs), and the T14's `proctreecase` boot of 9885370 is judged green by it, `process_tree` exiting 0. What the count meant to hold, a spawn refused after its admission giving back what admission took, is held on the host by kernel/proclife's tree tests, and on the T14 by abuse_spawn_maps, whose refusals come after admission and whose honest spawn runs after every one. The job keeps its own assertion that 64 zero bytes start nothing. The sighting of tty_interrupt's stall this branch added is dropped: main closed that record (ecb718f). The kernel's inventory, which main wrote while this was in review, no longer lists a child's arguments and environment as still held, and its ranking and sums lose that row. kernel/src is 215 files and 55,239 lines on this tree, from main's 55,379. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…is refused In the caller, before the kernel hears of it, not by the loader after admission. Three lines for three, so no panic location moves. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The kernel copied a spawn's argv and environment, wrote the argv onto the child's stack, kept the environment for the child's life and answered it through SYS_GET_ENV. None of it was the kernel's to read. A spawn is already the parent handing the kernel ranges to map, so what a parent tells its child is now one of them: a startup block (toyos::startup) of argc, the argv and envp pointers and their strings, which the parent writes into a memory object and lays out directly below the program. Every copy of the SDK in the child reads it at that one address, so a library's own std sees the arguments and the environment too. PWD, the starting directory, travels in it as before. The kernel loses SpawnArgs' argv and env fields, SYS_GET_ENV, ProcessData::env, the stack block it wrote and the charge for copying them; AddressSpace::fill and wrote_code, whose one caller was that stack write; and the working directory's MAX_PATH charge #1182 left behind. A child's stack starts empty. A process's name is the last component of its path, which the spawn's record and a crash's frames already name the file by: the kernel keeps that one label and no other text of the child. lay_out lays every program at BASE with an empty block below it, so the supervisor, which the build lays out and the kernel starts, and a raw spawn that says nothing start with no argument and no environment. Program::starting_with names a block in its place. The block's pages and the reserved ones above them keep a program's ranges one span, which a crash record's frames are judged against. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
kernel/src holds 215 files and 55,245 lines, from 215 and 55,386. The startup block's reservation is the module's own constant. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
startup_block's children exit with a code of their own once their checks hold, and the parent asserts each: a child told to read 300 KiB that found nothing took the other child's role and exited 0, which was accepted. issues/no-user-address-is-drawn-per-spawn.md records the startup block as a fourth constant address, the one whose bytes another party chooses, with its exit: it moves with the image. toyos::startup has one constructor, from arguments and environment, so no caller can hand Program::starting_with a block past its reservation. The track says the kernel keeps two userland texts, the endowment labels being the other. abuse_spawn_maps moves a program's range past the user half, not the block's page. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The T14's boots at cf06504 passed as boots and three members did not: - spawn_child_ends_first and abuse_handle_table aborted (exit 134): each fills its handle table and then spawns, and the helper made the child's startup block inside that spawn, where no slot is left for its object. Each now lays its child out, block and all, before the table fills, as it already read its image. - process_tree panicked: it looks in the roster for the `cat` its shell detached, and a launched process now goes by its row's file, `toybox`, which `cat` is a link to. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Naming a process for the last component of its path made every launched applet `toybox` in the roster, its exit record and a crash dump, which is a loss for `ps` and for reading a crash. SpawnArgs carries the name instead: 1 to 27 bytes of UTF-8 holding no control character and no white space (toyos_abi::syscall::process_name), bounded before it is copied and refused InvalidArgument otherwise, so a name is one token of one line wherever a record prints it. The SDK names a child for its argv[0]'s last component, as the kernel did, cut and cleaned to what the kernel takes. The name is a label and nothing trusts it. The kernel prints it and answers it in the roster. Log attribution is by the ring a record came out of, which the supervisor registers; the supervisor's rows, rights and restarts go by the handles it holds. A thread could already rename itself. process_tree looks for `cat` again. abuse_spawn_path is abuse_spawn_labels and holds the name's bound and shape beside the path's. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
issues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.md
is closed. Its forged name, `test_rs_job pid=1 code=0`, is refused at spawn:
a name is 1 to 27 bytes of UTF-8 with no control character and no white
space (toyos_abi::syscall::process_name), so it cannot carry a record's
separators. The claim is held by sdk/abi's
`a_process_name_is_one_bounded_token` on the host and by
abuse_spawn_path_and_name's two-word and two-line names on the T14's boot.
`make_name`, which the record cited, went with the name field.
The same forgery through the path is not closed and has its own record,
issues/a-spawns-record-quotes-the-path-its-caller-states.md: `spawn: {path}
pid=` writes a label its caller states, unchecked and unescaped. Read, not
run.
build/bootlog.rs and tests/common/metal.rs say where a recorded name comes
from now, the parent's SDK, and what `recorded_name` must agree with.
abuse_spawn_labels is abuse_spawn_path_and_name, since "labels" is the
endowment blob in this ABI. The SDK's `name` is private. The kernel spells a
name's array one way, THREAD_NAME_LEN, which is the roster's NAME_BYTES, and
`name_str` no longer answers `?` for a name that cannot be.
The stall's sighting says the run's serial artifact ends at the console's
hand-over and holds no exit record of the nested shell.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The T14's judge counted two kernel records, `spawn: /tmp/process_tree_invalid_elf: …`, one per B. Since #1189 the spawner lays a program out, so 64 zero bytes are refused in the caller and the kernel hears of no spawn: nothing prints that line, and the judge was red on every boot whose job passed. The job itself asserts the refusal in each B, which A's checks cannot pass without, so the count goes. The depth refusal, which only the kernel says, stays. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…'s record says its worst case build/bootlog.rs held kernel/src/process.rs to the text `THREAD_NAME_LEN: usize = 28`, which 9885370 changed, so `the_kernel_writes_the_records_the_host_reads` was red on every host from that commit. NAME_LEN is toyos_abi::inventory::NAME_BYTES, the constant the kernel's is defined as, and the needle goes; the test's doc no longer says the kernel truncates a name or that nothing links the crates. issues/a-spawns-record-quotes-the-path-its-caller-states.md states the line break: a path carrying a newline and a whole kernel record puts a line in /log that a judge reads as a verdict, from a spawn that need not succeed. Its exit is the orchestrator's decision in two parts, one for the forged line and one for the stated path. The stall's sighting gives the three UART logs' ends as they are. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…hat #1200 took issues/process-trees-metal-judge-waits-for-a-kernel-line-no-spawn-prints.md is closed by its exit's second way: the judge's count is gone (`process_tree` in tests/toyos.rs), and the T14's `proctreecase` boot of 9885370 is judged green by it, `process_tree` exiting 0. What the count meant to hold, a spawn refused after its admission giving back what admission took, is held on the host by kernel/proclife's tree tests, and on the T14 by abuse_spawn_maps, whose refusals come after admission and whose honest spawn runs after every one. The job keeps its own assertion that 64 zero bytes start nothing. The sighting of tty_interrupt's stall this branch added is dropped: main closed that record (ecb718f). The kernel's inventory, which main wrote while this was in review, no longer lists a child's arguments and environment as still held, and its ranking and sums lose that row. kernel/src is 215 files and 55,239 lines on this tree, from main's 55,379. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…is refused In the caller, before the kernel hears of it, not by the loader after admission. Three lines for three, so no panic location moves. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The rebase's resolution of the inventory left conflict markers in the commit before this one. The section is main's again, with what #1200 did marked done as #1210 marked its own, and the track's measurement says the kernel this branch leaves. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
6f78518 to
8670c0d
Compare
|
Orchestrator verification at 8670c0d, after review r4's SEND BACK at ebf9269 (two items, no code).
The squash takes the PR body. Queueing. |
… unchanged soundserver's bell arrives READ|WAIT, so the wait side really does go when its client's table does, as the comment beside it claimed. `RIGHTS_UNCHANGED` is `TransferEntry::UNCHANGED`: `SYS_HANDLE_DUP`'s word for the source's own set is u32::MAX where it was u64::MAX, and its two SDK wrappers are the only writers. The manual says a holder keeps a handle until it gives it up or ends. issues/a-received-handle-has-no-knowable-type.md records that it is taken up as (handle, ObjectKind) at receive, the three constraints its review set, and that event sides widen its reach. The spawn-side fixes of this round follow in the rebase onto #1200. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Every manifest-granted SysCap arrived carrying TRANSFER, added by `syscap_rights` because an endowment could not narrow: the supervisor had to keep the right on the duplicate to move it at all. A grantee could hand its grant to a process the manifest never named, and for `rt` the receiver entered the real-time band itself. The endowment's rights word is what removes it. `syscap_rights` is now exactly what the capability arrives with, and the supervisor endows a duplicate narrowed to it. Only `dup` lets a capability leave its program, so that name is DUP|TRANSFER: handing a cap on to a child takes duplicating it and moving the duplicate. The spelling: `toyos::spawn::Spawn::endow` carries `Moved`, and std's `CommandExt::endow_narrowed` takes the rights' bits, since std's own `toyos-abi` is not its caller's. A launch's job goes with the rights it has, and one named narrowed is refused. The kernel decodes a spawn's request as `SpawnEndow`s once, in `build_child_handles`, where it read a byte window twice by hand; the size assert that tied the request to `EndowEntry` goes. The loader's boot names, which the supervisor only maps, arrive MAP alone. The two boot images keep what they had: the supervisor hands them to the file server. `moved_rights` follows #1200's `SpawnArgs`. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A whole `testcases` boot on the T14 at 23f692c showed two shared-boot members red. Neither runs anywhere but that shared boot. 206_libc_refusals expected `sysconf(_SC_ARG_MAX)` 65536, the bound the kernel once put on each of a spawn's argv and environment. Since #1200 libc answers `toyos::startup::MAX_BLOCK`, 2 MiB less a page, 2093056 on both architectures: the most a child's startup block holds of its arguments, its environment and a pointer to each. That is the value libc means. served_file_offset asked `Helper::spawn(None)` for the kernel's `InvalidArgument`. Since #1189 the SDK refuses a spawn with no image, `Refused::Invalid`, before anything is duplicated, so the arm no longer reached what it holds: that a spawn the kernel refuses after the SDK duplicated its descriptors leaves the caller its export and the shared offset. It now spawns a real image placed under a pipe's write end, which carries `WRITE` but names no process: `SYS_SPAWN` lays out the program, then refuses that place `InvalidArgument`, as it refuses any wrong type of handle a caller names as a place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
The kernel copied every spawn's argv and environment, wrote the argv onto the child's stack, kept the environment for the child's whole life (
ProcessData::env) and answered it throughSYS_GET_ENV. It read none of it butargv[0], for the process's name. Since #1189 a spawn is the parent handing the kernel ranges to map, so this was the last userland data the spawn path carried (issues/the-kernel-is-small-interrupts-post-and-threads-wait.md, step 9's file half).Resulting behavior
toyos::startup):argc, theargvandenvppointers and their strings, which the parent writes into a memory object and lays out directly below the program. The kernel copies its pages as it copies the program's and reads none of it.PWD, the starting directory, travels in it as before.std::env, libc'senviron, and the working directory's owner. A library's own std sees the arguments too, which the per-copyARGC/ARGVstatics never showed it.SYS_GET_ENV(73) andget_env,SpawnArgs'argv_*andenv_*,ProcessData::env, the kernel's stack block and the charge for the copies, andAddressSpace::fillwith bothwrote_codes, whose one caller was that stack write. Also theMAX_PATHcharge per process that Step 9's file half: the kernel keeps no working directory; a spawn states it in the child's PWD #1182 left behind when the working directory went.kernel/src: 214 files and 55,303 lines on main (ad5db5a), 214 and 55,163 on this branch (git ls-files 'kernel/src/*.rs', lines bywc -l).Decisions a reviewer should weigh
SpawnArgscarrying one opaque word delivered in the first argument register. A fixed address needs nothing of the kernel and needs no symbol shared between the SDK copies in a process. Its cost: a spawner that lays out no block must still lay out the page, solay_outlays every program with an empty block (a page of zeros), which is what the supervisor and a raw spawn start with: no argument, an empty environment._SC_ARG_MAXsays so.lay_outlost itsatparameter. Every caller passedBASE, and the block's place is defined against it.SpawnArgs::name_ptr), by the orchestrator's decision after the T14 showed what naming it for the path cost: every launched applet wastoyboxin the roster, its exit record and a crash dump. The SDK names a child for itsargv[0]'s last component, as the kernel did.toyos_abi::syscall::process_name). The length is checked before anything is copied; anything else isInvalidArgument. A name is therefore one token of one line wherever a kernel record prints it, whichargv[0]never was; this closesissues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.md. The roster is not such a record: it answers a thread's own name, whichSYS_SET_THREAD_NAMEtakes unvalidated, ahead of the process's.lib/inspectprints; no right, claim or lookup reads it, and a thread could already rename itself throughSYS_SET_THREAD_NAME. Log attribution is by the ring a record came out of, which the supervisor registers on a connection only it holds, not by the name. The supervisor matches rows, grants rights and restarts by the handles it holds and never reads the roster. The roster's readers areps, osbench's report and tests.SpawnArgs::path_ptr), up to 64 KiB of userland text per process: the spawn's record and a crash's frames name the file by it, andsymbolizeopens that file. Removing it needs frames named by build-id and a userland index from build-id to file, which is not this change. Recorded in the track.Not changed: an empty argument is still dropped, now by the block's writer where the kernel's split did it (
issues/a-spawn-drops-an-empty-argument.md, updated). A non-UTF-8 argument is no longer refused by the kernel.Checks
All logs: https://github.com/ToyOSOrg/evidence/tree/8567264843bac075add2a7862892c8e306ca9b6e (its README tabulates command, revision, target and exit).
libc_children,session_lifecycle,a_launch_gives_each_unsent_stdio_slot_a_null,console_image_boots(x86-64 under KVM),virt_user_mode(AArch64 emulated)cargo run -- --ci host, lint included, macOS arm64clippy, warnings deniedis clean.the build system: 384 passed, 1 failed,llvm::tests::the_caller_s_environment_reaches_neither_the_build_nor_the_keywith the child it runs, red on every macOS host (issues/the-llvm-environment-test-breaks-git-on-macos.md). The log has 75controlsteps, of which 73 printFAILED; no other step holds one. loglibc_childrenonce at e9bbb29, bytty_interrupt's stall, whose record main has since closed (ecb718f). logsWhat the T14 shows
startup_block: the child given a block exited 41 and the child given none 42, as the parent asserts.abuse_spawn_path_and_name: each of the seven refusals said, then the longest name started a child.process_tree: exit 0, with the launched applets recorded ascat, and judged green by the repaired judge.abuse_handle_table,spawn_child_ends_first: exit 0, where cf06504's aborted.abuse_spawn_maps,abuse_page_straddle,spawn_image_object,spawn_lands_claimed,start_refuses_itself: exit 0.What ran on what
build/bootlog.rs(const NAME_LEN: usize = toyos_abi::inventory::NAME_BYTES;where it was28; the needle("kernel/src/process.rs", format!("THREAD_NAME_LEN: usize = {NAME_LEN}"))removed) andtests/toyos.rs(fn process_tree: theunloadedcount and itsif, six lines, removed). The rest is comments,process_tree.rs's header among them, and issue records.git range-diff): every conflict was in an issue record. Where a boot came from is the supervisor's to read, not the kernel's #1210 touchessdk/abi/src/syscall.rs,kernel/src/syscall/dispatch.rsand the supervisor's first spawn beside this branch's hunks, and they merged without conflict; the guest rows and the host checks above ran on that combined tree, and the T14 did not.Review
startup_block's two exit codes; the block recorded inissues/no-user-address-is-drawn-per-spawn.md; all suggestions.issues/a-process-name-is-written-into-the-kernels-own-records-unsanitised.mdclosed; the two comments; all suggestions.NAME_LENis the ABI's (my miss: I reported one failing test at 9885370 where there were two);issues/a-spawns-record-quotes-the-path-its-caller-states.mdstates the forged line and carries the orchestrator's two-part exit, neither part implemented here.process_tree.rs's header says B's first spawn is refused in the caller, before the kernel hears of it.process_tree's judge no longer counts a kernel line no spawn prints since The loader is the program's, and the kernel parses no ELF #1189, andissues/process-trees-metal-judge-waits-for-a-kernel-line-no-spawn-prints.mdis closed. The job asserts that refusal. A spawn refused after admission giving back what admission took is held on the host bykernel/proclife'sinterleave::tests::a_spawn_racing_its_places_kill_leaves_nothing_under_it_and_publishes_it, which the controlmutate-refused-spawn-keeps-the-countreds, and on the T14 by the three ofabuse_spawn_maps' sixteen refusals that come after admission.Two commit messages on the branch say what the tree no longer does, and the squash should take this body: the round 3 commit's last sentence describes a sighting hunk a rebase dropped with its record, and the commit before the last carried conflict markers in the track's record, which the last commit removes.
Unresolved
mask_windowsrun was made.🤖 Generated with Claude Code