Repository navigation
One table reads every refusal, each runtime keeps one from it to its word, and _SC_ARG_MAX is what a caller may pass - #1227
Conversation
fc819b2 to
22440c6
Compare
Review r1 of #1227 at 22440c6 (on 67713cb)Architecture
The table is complete and has no catch-all.
No fork change is needed. At the pinned rust commit a189b3a6, the fork calls only Main's changes since the base break nothing. Main 45935f4 adds no variant to The
POSIX semantics. XSH exec says it is "implementation-defined whether null terminators, pointers, and/or any alignment bytes are included" in The host test builds the block the way a spawn does today. FindingsBLOCKER: sdk/toyos/src/failure.rs:96-109 and 270-312, and sdk/libc/arch/src/errno_codes.rs:30-42.
BLOCKER: sdk/toyos/src/failure.rs:92-93 and 253.
BLOCKER: sdk/toyos/src/spawn.rs:37 and 517. The comments "counted as POSIX's SuggestionsSUGGESTION: spawn.rs:509-530. The test duplicates SUGGESTION: sdk/std/sys/pal/mod.rs:93-104. SUGGESTION: Sighting, not this PR: netstack's connect with no route (system/netstack/src/serve.rs:263) still answers Coordination note: this branch and #1226 overlap at sdk/libc/src/posix_io.rs EvidenceGuest run reuse is valid. Run 43036792480 at 55a94c8 succeeded, 10 of 10 rows. Host evidence. LAND-pending condition: metal. Run the T14
Line counts. Production grows about +170 lines (+667/−498), from one table and two runtime tables replacing ten mappers. Tests grow about +200 lines. Records go −193/+10. SEND BACK |
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ea69cd0 to
2795520
Compare
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntrols and metal staging; masked
Response to review r1 (head 2795520, rebased onto e558cc1)
Checks: the targeted host tests and clippy exit 0 at 2795520, and the |
Review r2 of #1227 at 2795520 (on main e558cc1)Scope: r1's findings and the r1 commit's code. ArchitectureUnchanged: the SDK keeps one vocabulary of failures, and each runtime keeps one table from it. The split costs three variants and makes each r1 findingsBLOCKER, BLOCKER,
BLOCKER, "POSIX's count": resolved. spawn.rs:36-42 and the test's comment now say the count is ToyOS's and that POSIX leaves it to the implementation, as XSH exec says. Suggestions.
New findingBLOCKER: sdk/toyos/src/startup.rs:84 with sdk/toyos/src/elf/program.rs:48-51.
Evidence
A guest dispatch is owed before the T14 boot.
LAND-pending after the fix:
SEND BACK |
Block::new took any byte slice once it stopped composing the block itself, so a Block no longer promised a laid-out block of at most MAX_BLOCK bytes, which Program::starting_with relies on when it reserves from the block's end up to the program. A public caller could make the SDK's spawn overflow that subtraction, or hand a child pointers it cannot follow. startup::bytes now answers Said, whose field is private and which only it makes, and Block::new takes a Said. starting_block answers one, and the callers read as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… staging; masked
Response to review r2 (head 7826f4f)
|
The corpus runs only on the T14's testcases boot, and no bench can boot. The full-table record, whose corpus case is 212_full_table, carries what main must show there when a bench returns: every member green, 212_full_table's ENOMEM, 206_libc_refusals' _SC_ARG_MAX of 2088939 and the watchdog boot green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r3 of #1227 at 7826f4f (on main e558cc1)Scope: r2's blocker, the one commit since 2795520, and the PR body. ArchitectureThe fix puts the guarantee back in the type, in the module that owns the layout. r2 blocker: resolved
New findingsBLOCKER: the PR body and the branch's issue records. The body still makes metal a landing condition, and no record says what metal owes.
SUGGESTION: startup.rs:48. PR body, otherwise
Evidence
SEND BACK. The code is finished. Still needed: the owed-on-metal record, the matching body correction, then a green 43084578853. |
|
Orchestrator note on review r3: its one blocker (no owed-on-metal record; body still made metal a landing condition) is already resolved at 648a40c, a records-only commit on 7826f4f (verified: +7 lines in |
|
Orchestrator: landing under the benches-down rule. Review r3's only blocker (owed record) was already resolved at 648a40c (verified). Guest run 43084578853 at code-identical 7826f4f: success. Corpus checks ( |
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
648a40c to
f341be8
Compare
Block::new took any byte slice once it stopped composing the block itself, so a Block no longer promised a laid-out block of at most MAX_BLOCK bytes, which Program::starting_with relies on when it reserves from the block's end up to the program. A public caller could make the SDK's spawn overflow that subtraction, or hand a child pointers it cannot follow. startup::bytes now answers Said, whose field is private and which only it makes, and Block::new takes a Said. starting_block answers one, and the callers read as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The corpus runs only on the T14's testcases boot, and no bench can boot. The full-table record, whose corpus case is 212_full_table, carries what main must show there when a bench returns: every member green, 212_full_table's ENOMEM, 206_libc_refusals' _SC_ARG_MAX of 2088939 and the watchdog boot green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lier landing run; masked
|
Orchestrator verification of the rebase onto #1226 (f341be8): |
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
f341be8 to
96b79b6
Compare
…s and build; masked
|
Orchestrator verification of the rebase onto main 3c5c555 at a92cc30: I read the range-diff against 96b79b6. Commits 2–5 are
The resolution is mechanical, so the queued run 43122100459 at 96b79b6 (hold branch Composition note: #1258 adds |
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a92cc30 to
b2946a6
Compare
Block::new took any byte slice once it stopped composing the block itself, so a Block no longer promised a laid-out block of at most MAX_BLOCK bytes, which Program::starting_with relies on when it reserves from the block's end up to the program. A public caller could make the SDK's spawn overflow that subtraction, or hand a child pointers it cannot follow. startup::bytes now answers Said, whose field is private and which only it makes, and Block::new takes a Said. starting_block answers one, and the callers read as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The corpus runs only on the T14's testcases boot, and no bench can boot. The full-table record, whose corpus case is 212_full_table, carries what main must show there when a bench returns: every member green, 212_full_table's ENOMEM, 206_libc_refusals' _SC_ARG_MAX of 2088939 and the watchdog boot green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t checks, controls, build; masked
Review of #1227's rebase onto #1252 at b2946a6Scope: the conflict resolutions against #1252, the new commit b2946a6, and the ArchitectureThe rebase keeps the settled design. The names cap sits in the owner of the layout: Resolutions
|
… and std's word for too big is E2BIG's #1271 made the testcases shared boot a guest row (testcases_members_exit_0), which runs every Rust member and the C corpus on QEMU, so the records' "no guest row runs the corpus" and "the member runs only on metal" were false. The full-table record now owes metal only the testcases-watchdog boot, which no guest row runs. The record of the stream state's TCP half owes stream_state_is_one's run at main to that guest row, since what it holds is no bench's. std answers Failure::TooBig ArgumentListTooLong, std's own kind for E2BIG, which libc answers, rather than InvalidInput. The MAX_ARGS test's doc says its names table is MAX_NAMES bytes, MAX_NAMES's doc keeps only its capacity, and the record of a dropped empty argument names startup::bytes as the writer that skips it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st checks and build; masked
|
Orchestrator verification at c40194e, from b2946a6, which the rebase review read:
Everything except S1's arm is unchanged code, so run 43156966054 at b2946a6 stays the landing run. #1227 is queued when it is green. |
…its word libc and std each kept a table per source of refusal: the kernel's word, a file server's, the descriptor table's, a stream's, netstack's, a spawn's. Several fell through: libc answered EINVAL for a kernel word it did not name and EIO for half of netstack's, std answered Other for a device's failure, for netstack's limit and for a lookup that found no address, getaddrinfo answered -1 for everything, and fread, fwrite and fseek set no errno at all. toyos::Failure is the one vocabulary. sdk/toyos/src/failure.rs reads every word of SyscallError, fs::Error, files::Error, fd::Error, NetError, a stream's Refused, spawn::Refused, ImageRefused and dl::Refused into it by name, with no catch-all, so a word added to any of them fails to compile until it is read. libc keeps one table from Failure to errno (errcode::of) and std one to io::ErrorKind (sys::failed), both exhaustive. A call whose POSIX page has another word for a refusal names that refusal and sends every other through the table (mprotect's and madvise's ENOMEM for pages not held). getaddrinfo answers POSIX's EAI words from the same failure. What a caller sees change: - libc: a bad address is EFAULT and an unknown kernel word EIO, where both were EINVAL; no netstack is ENOTCONN and netstack's limit EAGAIN, where both were EIO; a launch that started nothing is EIO, not EACCES; fread, fwrite, fseek and ftell set errno. - std: a device's failure is Uncategorized, as every other platform says it, not Other; netstack's limit is QuotaExceeded; a name with no address is NotFound, apart from every failed lookup; kill, wait and try_wait answer the kernel's word. - the shell says "not found" for NotFound alone and prints any other refusal of a spawn. The pipe ABI gains three words at netstack's boundary, 10 to 12: ERR_NETWORK_UNREACHABLE, ERR_HOST_UNREACHABLE and ERR_TRY_AGAIN. netstack answers a connect an ICMP destination unreachable ended by what its code names (the network, the port as a refusal, else the host), a prohibited or time-exceeded one as the host, a lookup with no way out as the network and one whose lease changed as try again. osbench's lookup retry, the one caller that waited on the old word, waits on the new ones too. std's fd_error stays as a one-line name over the table: the rust fork's BorrowedFd::try_clone_to_owned calls it, and renaming it is a fork commit this change does not need. Records: closes libc-answers-einval-for-every-error-it-does-not-name, libc-loses-the-kernels-word-on-two-write-paths, std-maps-a-device-error-to-other-not-uncategorized, std-net-maps-every-other-syscall-error-to-other (its syscall_err was already gone), std-lookup-host-answers-no-address-as-other, the-pipe-abi-has-no-word-for-an-unreachable-host-or-a-lookup-to-try-again and the-shell-says-not-found-for-every-spawn-error. a-full-kernel-handle-table-answers-enomem-not-emfile stays: the kernel says one word for a full table and for no memory, and the owner ruled no kernel change for POSIX alone. 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>
… restates a match spawn::Refused::Invalid carried every refusal a spawn made of itself, and Failure read all of them as TooBig, whose contract, a spawn carrying more than one carries, was false for most: an image that does not lay out answered E2BIG where POSIX's word is ENOEXEC. The variant is split. An image no program lays out is NotExecutable (ENOEXEC, std InvalidData), a spawn or a launch past what it carries is TooBig (E2BIG), and the rest stay Invalid, which is now InvalidArgument (EINVAL). libc's POSIX-name test holds the three. Helper::spawn's block is one function, starting_block, which states the working directory and lays out the block, and the MAX_ARGS test calls it, so the limit and what a spawn composes cannot drift. Block::new takes the block's bytes, startup::bytes is public, and startup's Refused is gone. The two guest tests that build a block call startup::bytes first. Failure::ALL is gone with the four tests that held what exhaustive matches already hold: every failure listed once, every kernel word and every file protocol word a failure, and every failure's errno one errno.h defines. The pipe-word test, libc's POSIX-name tests and the netdb.h test stay. MAX_ARGS's comments say the count is ToyOS's: POSIX leaves whether pointers and terminators count to the implementation. std keeps the specific text of a stream's own handle moved into a spawn (Misuse::Moved) and of a stream this process holds no end of (Misuse::NotThisProcess, through stream_failed for TcpStream's calls). Records the reviewer's sighting: a connect with no route answers ERR_NOT_CONNECTED, where POSIX's word is ENETUNREACH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Block::new took any byte slice once it stopped composing the block itself, so a Block no longer promised a laid-out block of at most MAX_BLOCK bytes, which Program::starting_with relies on when it reserves from the block's end up to the program. A public caller could make the SDK's spawn overflow that subtraction, or hand a child pointers it cannot follow. startup::bytes now answers Said, whose field is private and which only it makes, and Block::new takes a Said. starting_block answers one, and the callers read as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The corpus runs only on the T14's testcases boot, and no bench can boot. The full-table record, whose corpus case is 212_full_table, carries what main must show there when a bench returns: every member green, 212_full_table's ENOMEM, 206_libc_refusals' _SC_ARG_MAX of 2088939 and the watchdog boot green. 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>
… and std's word for too big is E2BIG's #1271 made the testcases shared boot a guest row (testcases_members_exit_0), which runs every Rust member and the C corpus on QEMU, so the records' "no guest row runs the corpus" and "the member runs only on metal" were false. The full-table record now owes metal only the testcases-watchdog boot, which no guest row runs. The record of the stream state's TCP half owes stream_state_is_one's run at main to that guest row, since what it holds is no bench's. std answers Failure::TooBig ArgumentListTooLong, std's own kind for E2BIG, which libc answers, rather than InvalidInput. The MAX_ARGS test's doc says its names table is MAX_NAMES bytes, MAX_NAMES's doc keeps only its capacity, and the record of a dropped empty argument names startup::bytes as the writer that skips it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c40194e to
31855f8
Compare
…checks and build; masked
|
Orchestrator verification at 31855f8, the rebase onto main ee15a2e (#1258). I read the range-diff against c40194e:
The landing run is 43156966054 at b2946a6. The same code plus compile-settled arms passed every row except two |
…he switch carries FS and GS (#1264) No conflicts. #1227 removed libc's `refuse_files`: every refusal now goes through `set_errno`, which reads `toyos::Failure`'s one table. A refused map of a file answers through it too. A file its server maps no pages of still answers `ENODEV`, as POSIX names that refusal of `mmap`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
libc and std each kept their own table for every source of refusal: kernel words, a file server's, the descriptor table's, a stream's, netstack's and a spawn's. Several fell through:
Otherfor a device failure, for netstack's limit and for a lookup that found no address.getaddrinfoanswered -1 for everything.fread,fwriteandfseekset no errno.Resulting behaviour
toyos::Failure(sdk/toyos/src/failure.rs) is the single vocabulary at the SDK boundary. It reads every word ofSyscallError,fs::Error,files::Error,fd::Error,NetError, a stream'sRefused,spawn::Refused,ImageRefusedanddl::Refusedby name, with no catch-all. A word added to any of those types does not compile until it has aFailure. libc keeps one exhaustive table fromFailureto errno (errcode::of), and std keeps one toio::ErrorKind(sys::failed). Where a call's POSIX page gives a refusal a different word, the call names that refusal itself and sends every other refusal through the table.getaddrinfoanswers POSIX'sEAI_*words from the same failure.Changes a caller sees:
fread,fwrite,fseekandftellset errno.Uncategorized, as other platforms report it, notOther. A spawn past what one carries isArgumentListTooLong, std's kind forE2BIG, notInvalidInput. A stream's own handle moved into a spawn, and a stream this process holds no end of, keep their specific texts (Misuse::Moved,Misuse::NotThisProcess). Netstack's limit isQuotaExceeded. A name with no address isNotFound.kill,waitandtry_waitanswer the kernel's word.NotFoundand prints any other spawn refusal.spawn::Refused::Invalidis split.NotExecutableis an image that does not lay out,TooBiga spawn or launch past what it carries (its arguments and environment, its endowments, its names table paststartup::MAX_NAMES, a launch's provided connectors, one launch frame), andInvalidthe rest.ERR_NETWORK_UNREACHABLE(10),ERR_HOST_UNREACHABLE(11) andERR_TRY_AGAIN(12).No netstack stays
ENOTCONNandNotConnectedrather thanENETDOWNandNetworkDown, as std answered before. sshserver's quiet exit on a machine with no network, osbench and thenetstack_gone_mid_bindmember all key on that kind, so changing it would move three consumers for a better name and fix no defect.std's
fd_errorstays as a one-line name over the table, because the rust fork'sBorrowedFd::try_clone_to_ownedcalls it. This change does not need a fork commit._SC_ARG_MAX(added to this PR by the orchestrator) reported the whole startup block,toyos::startup::MAX_BLOCK. Since #1252 the block also holds the names' address, the argument count, thePWD=<cwd>entry the SDK states and the child's names for its endowments. The names table had no bound but the block, so a caller's room depended on what the spawn endowed. The changes:startup::MAX_NAMES(64 KiB), refused as a spawn too big past it. That is many times any table a spawn in this tree makes: as many names as a spawn endows, of about 680 bytes each._SC_ARG_MAXreportstoyos::spawn::MAX_ARGS: the block less its two words, the longest stated entry (a pointer,PWD=,fs::MAX_PATHbytes and a NUL) andMAX_NAMES, derived fromstartup::WORD,startup::MAX_BLOCK,files::MAX_STATEDandstartup::MAX_NAMES. A caller'sMAX_ARGSbytes therefore fit whatever a spawn endows.Helper::spawnbuilds its child's block with one function,starting_block, which the limit's test calls too with a names table ofMAX_NAMESbytes, so the limit and what a spawn composes cannot drift.startup::bytesanswersSaid, a block's bytes as it laid them out (at mostMAX_BLOCK, its field private), andstartup::Block::newtakes only aSaid, so aBlockstill holds nothingProgram::starting_withcannot reserve below the program.206_libc_refusals.expect, which tests: two shared-boot members assert what a spawn does on main #1219 set to 2093056, says so.Coordination
#1226 landed first, and this branch is rebased onto it (see Checks).
#1258 landed first with the kernel word
SyscallError::TextBusy(12). This branch addsFailure::TextBusyandSyscallError::TextBusy => Self::TextBusy, which libc answersETXTBSYand stdExecutableFileBusy, the words #1258 gave it. libc's POSIX-name test holds theETXTBSY.#1272 (in review) adds the file server's error
Unanswered, which libc answersETIMEDOUTand stdTimedOutin the old tables. If #1272 lands first, this branch's next rebase addsError::Unanswered => Self::TimedOuttoFrom<fs::Error> for Failure, and the existing tables give the same two words. If this branch lands first, #1272 does it.#1261 landed first with the kernel word
SyscallError::Busy. This branch addsSyscallError::Busy => Self::BusytoFrom<SyscallError> for Failure, so libc answersEBUSYand stdResourceBusy, the words #1261 gave it, through the existing tables. libc's POSIX-name test holds theEBUSY.Records
libc-answers-einval-for-every-error-it-does-not-name,libc-loses-the-kernels-word-on-two-write-paths,std-maps-a-device-error-to-other-not-uncategorized,std-net-maps-every-other-syscall-error-to-other,std-lookup-host-answers-no-address-as-other,the-pipe-abi-has-no-word-for-an-unreachable-host-or-a-lookup-to-try-again,the-shell-says-not-found-for-every-spawn-error.a-connect-with-no-route-answers-not-connected. netstack answers a connect with no routeERR_NOT_CONNECTED, where POSIX's word isENETUNREACH.a-full-kernel-handle-table-answers-enomem-not-emfilestays open. The kernel uses one word for a full table and for no memory, and the owner ruled out a kernel change made only for POSIX.Tests
FromintoFailureand both runtimes' tables are matches with no catch-all, so no test restates them.toyos::failure's pipe-word test holds netstack's codes, the three new ones among them, and one it does not send. libc'serrno_codestests hold that every code libc answers with iserrno.h's by name and value, the POSIX names of the kernel's, the descriptor table's, a spawn's, a file server's and netstack's refusals, andgetaddrinfo'snetdb.hwords.spawn::tests::max_args_of_a_caller_s_own_fit_beside_the_longest_directorycallsstarting_blockwith afs::MAX_PATH-byte directory for a caller's argv and envp of exactlyMAX_ARGSbytes. Those bytes are counted apart from the block's arithmetic, as ToyOS counts them. The test asserts the block fits, and that one byte more is refusedTooBig.testcases), plus the QEMU rows listed in the checks comment.Checks
Evidence, masked, with a README per round listing each command, exit code and revision: r10, r9, r8, r7, r6, r5, r4, r1, r2, r3.
Head 31855f8 is the branch rebased onto main ee15a2e, which has #1226, #1259, #1261, #1239, #1252 and #1258. On top of the rebase are a commit for the names table's bound and one for the rebase review: records corrected for #1271, std's
ArgumentListTooLong, and wording. The resolutions, oldest first:dup2insdk/libc/src/posix_io.rs. A process holds its stdio stream state once, with its descriptor table #1226 made it one line, because the table's owner forgets a stream's answer itself, and that line calledfd_errno, which this branch deletes. The resolution keeps A process holds its stdio stream state once, with its descriptor table #1226's line with this branch's table:unsafe { toyos::fd::dup2(old_fd, new_fd, true) }.unwrap_or_else(|e| refuse(crate::errcode::of(e))).Helper::spawn(sdk/toyos/src/spawn.rs),spawn_image_object.rsand the rust-testsimage.rs. 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 madelay_out's third argument aSource. This branch changed the block line before it (startup::bytes'sSaid) andlay_out's refusal word (NotExecutable), and the resolution keeps both. Every refusallay_outhas on main is still a property of the image.sdk/libc/src/errcode.rsandsdk/std/sys/pal/mod.rs: A poll belongs to the handle it was made through, and a ring to its one submitter #1261 added aBusyarm to the old kernel-word tables this branch replaces. Kept: this branch's tables, which already answeredFailure::BusyEBUSYandResourceBusy, and the newSyscallError::Busy => Self::Busyarm.sdk/libc/src/posix_io.rsopen_handle: A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so #1239 madefiles::export's answer.into_owned().into_raw(), and this branch made the line's refusalcrate::errcode::of; both are kept.SyscallError::TextBusy => ETXTBSYand=> ExecutableFileBusyto the old tables. Kept: this branch's tables, with a newFailure::TextBusymapped to those two words, theSyscallError::TextBusyarm and the test line. Commits 2 to 7 are patch-identical (range-diffin r10).range-diff-a92cc309e-b2946a672.txtin r7 has every line.startup.rskeeps A child's handles are a list its parent names: labels and namespaces leave the kernel #1252's layout: the names' address, the count, the pointers, the strings and the names table. This branch'sWORD,SaidandBlock::new(&Said, job)sit on it, andstartup::Refusedstays gone.Helper::spawnkeeps A child's handles are a list its parent names: labels and namespaces leave the kernel #1252's endowment table (the caller's endowments, inherited services and descriptors, by name) with this branch's words on its refusals:TooBig;Invalid;TooBig;NotExecutable.starting_blocktakes the names, and the block takes A child's handles are a list its parent names: labels and namespaces leave the kernel #1252'ss.job. A child's handles are a list its parent names: labels and namespaces leave the kernel #1252 deleted the label machinery this branch had relabelled.startup::bytes(.., names).MAX_ARGScounts the block's second word, and the new commit bounds the names table and counts it.Lands on: review LAND, the host checks below and guest run 43156966054 green at b2946a6. The review found the resolutions and the bound correct, and sent back text only. Beyond b2946a6, this head changes std's
TooBigarm (the review commit) and adds theTextBusymapping (the #1258 rebase), both compiled by the testcases build, plus doc comments and records. The run holds for the rest.cargo test -p toyos(startup's and theMAX_ARGStests among them),cargo clippy -p toyos --all-targets,cargo clippy -p toyos --lib --features fd-table,cargo test -p toyos-libc-copies,cargo clippy -p toyos-libc-copies --all-targetsandcargo test -p netstack, all exit 0. Clippy runs with the adopted lints and-D warnings. The host-built code touched issdk/toyos,sdk/libc/archand netstack; libc proper, std, the shell, osbench and the rust-tests members are guest-built.cargo run -- --build-only --boot-config tests/images/testcases.toml, exit 0. It compiled the sysroot (std and libc, which no host check compiles) and the testcases image. The rust-tests crate is built by the guest run'stestcases_members_exit_0.MAX_ARGSone byte smaller, one byte larger, and without the names table. Each was applied as a patch and built, and each failsmax_args_of_a_caller_s_own_fit_beside_the_longest_directory(r7).MAX_ARGSone byte under and one over. Each was applied as a patch and built, and each failsmax_args_of_a_caller_s_own_fit_beside_the_longest_directoryat one of its two assertions (r2).libc_children virt_user_copy_faults: exit 0, 2 of 2. It covers libc'sposix_spawnerrno path and every child spawned throughstarting_block, and it built and booted the aarch64 image.libc_children virt_user_copy_faults a_spawn_from_an_image_frees_it_when_the_child_ends testcases_members_exit_0, exit 1 (r9). The three rows passed.testcases_members_exit_0reports 240 of 242 members exiting 0. The two red members, diagnosed as main's and awaiting main's run 43155677796 of the same row at this branch's base:test_rs_abuse_listener_hijackgets the kernel's rawPermissionDeniedwhere it expectsInvalidArgument.test_rs_adopted_services_stay's adopter fails its assertion thattoyos::endow::services()lists the service it adopted. Its spawn succeeded.toyos::endowis changed here, and the child's block bytes for these spawns are main's.testcases_members_exit_0runs thetestcasesshared boot on QEMU since The shared boots run as guest rows, each member judged by its own exit #1271: every Rust member, the two guest tests in the conflict among them, and the C corpus.206_libc_refusals(2023395) and212_full_table(ENOMEM) are among the 240 that exited 0.issues/a-full-kernel-handle-table-answers-enomem-not-emfile.md): the T14'stestcases-watchdogboot at main after this PR, which no guest row runs.mainmust show it green. Thetestcasesmembers,212_full_tableprintingENOMEMand206_libc_refusalsprinting 2023395 among them, are the guest rowtestcases_members_exit_0's, which run 43156966054 carries.cargo test --test toyos-build -- --metal --metal-readback <dir> boot:testcases, exit 2) built the x86_64 testcases image: the sysroot with this std and libc, every rust-tests member and the C corpus. The staged images are deleted.--ci host. The merge queue runs it.🤖 Generated with Claude Code