Repository navigation
tests: two shared-boot members assert what a spawn does on main - #1219
Conversation
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>
5efb81f to
a336ce8
Compare
Review of #1219 at a336ce8ArchitectureTest-only, +11 −4: one
POSIX lets the implementation decide whether the pointers and null terminators count toward ARG_MAX, and ToyOS counts them. The block also holds two things the caller did not send: the argc word, and the
SweepCredible and, for this class of test, complete.
Evidence
FindingsNo blockers.
LAND |
|
Orchestrator: verified the T14 readback at a336ce8 myself: |
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as POSIX counts them (each string with its NUL, a pointer to each and the null ending each list), had 21 + len(cwd) bytes less than reported, up to 4117 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its count word and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it: 2088939. A host test composes what Helper::spawn composes, the longest stated directory and then the block, for a caller's MAX_ARGS bytes, and one byte more is refused. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088939. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as POSIX counts them (each string with its NUL, a pointer to each and the null ending each list), had 21 + len(cwd) bytes less than reported, up to 4117 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its count word and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it: 2088939. A host test composes what Helper::spawn composes, the longest stated directory and then the block, for a caller's MAX_ARGS bytes, and one byte more is refused. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088939. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as POSIX counts them (each string with its NUL, a pointer to each and the null ending each list), had 21 + len(cwd) bytes less than reported, up to 4117 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its count word and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it: 2088939. A host test composes what Helper::spawn composes, the longest stated directory and then the block, for a caller's MAX_ARGS bytes, and one byte more is refused. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088939. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as POSIX counts them (each string with its NUL, a pointer to each and the null ending each list), had 21 + len(cwd) bytes less than reported, up to 4117 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its count word and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it: 2088939. A host test composes what Helper::spawn composes, the longest stated directory and then the block, for a caller's MAX_ARGS bytes, and one byte more is refused. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088939. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as POSIX counts them (each string with its NUL, a pointer to each and the null ending each list), had 21 + len(cwd) bytes less than reported, up to 4117 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its count word and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it: 2088939. A host test composes what Helper::spawn composes, the longest stated directory and then the block, for a caller's MAX_ARGS bytes, and one byte more is refused. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088939. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the names' address, the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as ToyOS counts them (each string with its NUL, a pointer to each and the null ending each list), had 29 + len(cwd) bytes less than reported, up to 4125 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its names' address and count words and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it. A host test composes the longest stated directory and then the block, with no names, for a caller's MAX_ARGS bytes, and one byte more is refused. The names table is counted in a later commit. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088931. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y come to sysconf(_SC_ARG_MAX) answered the whole startup block, MAX_BLOCK. A helper's block also holds the names' address, the argument count and the PWD=<cwd> entry the spawn states (files::starting_in), so a caller's own argv and envp, counted as ToyOS counts them (each string with its NUL, a pointer to each and the null ending each list), had 29 + len(cwd) bytes less than reported, up to 4125 with a MAX_PATH directory. toyos::spawn::MAX_ARGS is the block less its names' address and count words and the longest stated entry: a pointer, PWD=, MAX_PATH bytes and a NUL, from startup::MAX_BLOCK, startup::WORD and files::MAX_STATED, so the limit and the block cannot drift. libc answers it. A host test composes the longest stated directory and then the block, with no names, for a caller's MAX_ARGS bytes, and one byte more is refused. The names table is counted in a later commit. 206_libc_refusals.expect, which #1219 set to MAX_BLOCK's 2093056, says 2088931. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
A whole
testcasesboot on the T14 at 23f692c (#1204's images, which hold#1189 and #1200) showed two shared-boot members red
(judge,
their lines):
206_libc_refusalsexit 1:sysconf _SC_ARG_MAX: 2093056produced against65536expected.test_rs_served_file_offsetexit 101:assertion failed: matches!(helper.spawn(None), Err(Refused::Syscall(InvalidArgument))).Neither runs anywhere but that shared boot, and #1200's own T14 boots ran
nine other members (and
process_tree).Resulting behavior
206_libc_refusals.expectsays2093056. Since A child's arguments and environment are memory its parent lays out #1200 libc answers_SC_ARG_MAXwithtoyos::startup::MAX_BLOCK, 2 MiB less a page: the mosta child's startup block holds of its arguments, its environment and a
pointer to each, which A child's arguments and environment are memory its parent lays out #1200 chose and its description says
_SC_ARG_MAXreports.
PAGE_SIZEis the ABI's one 4096, so the value is the same onboth architectures. 65536 was the bound the kernel once put on each of a
spawn's argv and environment.
served_file_offset's refused-spawn arm reaches the kernel again.Its claim: a spawn the kernel refuses after the SDK duplicated its
descriptors leaves the caller its export and the shared offset. Since
The loader is the program's, and the kernel parses no ELF #1189 the SDK refuses a spawn with no image (
Refused::Invalid) before itduplicates anything, so
spawn(None)never got there. The arm now spawnsa real image (
/system/bin/toybox, read asimage_ofreads it) placedunder a pipe's write end. That handle carries
WRITEand names noprocess, so
SYS_SPAWNlays out the program and then refuses the placeInvalidArgument(spawn_place: a wrong type of a sent handle is refused,not a fault). In this arm the SDK's one source of
Refused::Syscall(InvalidArgument)is that syscall, made after theduplicates exist, so the assertion also shows the arm reached the state
it tests.
The sweep: same class elsewhere
Looked for tests that encode a spawn refusal, an argument or environment
bound,
ARG_MAX, a process name or an.expectthat #1189 or #1200 changed.with an
.expect,LAST_MEMBERS, ontestcases;ACTUATOR_TESTSonshared-debug): checked by run, not inspection. The 23f692c bootsran all of them, 236 on
testcasesand 9 onshared-debug; these two werethe only members red. Between 23f692c and this base, main changed
comments only on any spawn, startup or libc path, and added no member.
RUST_SKIPbinaries that do notride
testcases, and the harness's judges): by inspection. None asserts avalue The loader is the program's, and the kernel parses no ELF #1189 or A child's arguments and environment are memory its parent lays out #1200 changed. Those that assert spawn or name behavior
ran green in A child's arguments and environment are memory its parent lays out #1200's own checks:
libc_children(everytests/guest/libcprogram and its five Rust probes),
session_lifecycle,a_launch_gives_each_unsent_stdio_slot_a_nullandprocess_tree.memory_floorexpects a spawn refused for memory to beOutOfMemory,which std's image read gives (
ResourceExhausted) before any startupblock is made.
.expectfiles exist only in the TinyCC corpus; every one that runsran in that boot.
sweep notes, the 58 binaries with their grep counts
Checks
cargo test --test toyos-build -- --metal --metal-readback <dir> boot:testcases, macOS arm64Stagedverdict (a staging run judges nothing, so it never exits 0). It built every x86_64tests/guest/rust-testsbinary,served_file_offsetwith no compiler diagnostic, compiled the C corpus and staged 236 shared members. log, notestestcasesimage searched for everysysconf _SC_ARG_MAX:line2093056(notes)testcasesboot (image sha2560544ef99ce710e44…)passed.===TEST_END test_rs_served_file_offset exit=0===afterserved_file_offset: passed, with no child started for the refused spawn;===TEST_END 206_libc_refusals exit=0===afterccheck: 0. All 256 jobs ended; the three nonzero exits are the same three as at 23f692c (test_rs_wake_onto_busy, a measurement;test_rs_i219_msi, recorded inissues/i219-msi-passes-only-when-the-network-sends-it-a-frame.md;test_rs_usbd_spare_vector, an AMD-bench member).back_secs98, under its ceiling of 100. The two members were judged by name from the kernel log; the harness'sboot:testcasesjudge also needs thetestcases-watchdogboot and was not run.No host check: neither file is built on the host.
tests/guest/rust-testsis outside the host workspace, so host tests and clippy never compile it,
and a corpus
.expectis read only when a boot is staged, which the stagingrun above did. The staging compile is that test's compile check.
No guest row: both members run only on the T14's shared boots, and the
sweep found no QEMU row whose assertion changed.
Risks
boot:testcasesalso takes every row with an arm on that boot, the AMDbench's among them. At 23f692c those rows failed on the T14 for the
machine (
idle_entered, theamdvi_*rows, andconsole_uart_drivenonusbd_spare_vector's panic), as didi219_messages_on_its_msi_vector;none is in this class.
boot.testcases.back_secswas also over itsceiling there (124 against 100, unattributed in A move names the rights it delivers: a ring and a manifest's SysCap stay where they were given #1204); it was 98 at this
head.
🤖 Generated with Claude Code