Repository navigation
SYS_FSTAT answers whether a region is memory the kernel allocated, and a lent region is judged by it before it is mapped - #867
Conversation
SYS_HANDLE_RECV writes a `Received { handle, kind }` per handle, the kind an
`ObjectKind` word the kernel's `kobject!` maps by variant name, so a row
without an ABI kind does not compile. `ObjectKind` replaces `OBJECT_KINDS`:
one list of kinds in the ABI, and the census reads it by kind, not by name.
SYS_SHM_MAP writes a `ShmMapping { addr, len, ram }`: the mapping's length,
which is the whole region, and `SharedMemObject::ram()`'s verdict — memory the
kernel allocated, as against a BAR or a scanout.
The SDK takes both. `recv_handles_exact` is told the kinds the protocol sends
and refuses any other batch, closing it, where a handle of the wrong kind used
typed ends the receiver (exit 139). `SharedMemory::lent` takes a region a
peer lent only if the kernel says it is RAM and exactly the protocol's length
in whole SHM_PAGEs; `adopt` is left to regions the kernel itself answered and
asserts the view fits the mapping. Every caller is moved: fileserver, diskserver,
logkeeper, soundserver's and the compositor's clients, netstack's piped
requests (whose pipe ends are now told apart by kind, so a reversed pair is
refused), the supervisor's launch batch, toyos::log's stdio ring, and the guest
tests. diskserver's oversize branch after `dma_map` goes: `lent` refuses that
region first.
Closes issues/a-received-handle-has-no-knowable-type.md and
issues/a-file-server-maps-a-lent-window-at-the-size-it-expects.md: the
`lent_window_refused` machine test lends fileserver a pipe end, a region
twice WINDOW_BYTES and the virtio NIC's BAR, each refused by name, and the
same fileserver answers the next hello.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Round 2: metal staging of lent_window_refused, m3 (temporary commit 941446b8f, reset away), EXIT=2 (staged), part 1 of 1 |
|
Round 2 at af89f49: netstack_socket_churn with --nocapture, EXIT=0; netstack's refused-watch arm at its line 279, part 1 of 1 |
|
T14 results for
m3 survives. Under m3 both hostile hellos are still let go (
|
|
Review of #867 at Net lines ( Round 1
Checked
BLOCKER
NOTE
SEND BACK |
|
Design rulings for this stage, recorded by the orchestrator. Both apply the owner's standing constraint on #863: the kernel gains only what it alone can answer, and gets smaller overall.
|
Round 3 of #867, on review round 2's BLOCKER and the orchestrator's ruling that accepts the narrowed design. SYS_FSTAT resolves a handle of any kind and needs no right, and only a region of memory the kernel allocated answers SharedMemory. So SharedMemory::lent, which reads fstat before it maps, already refuses every handle that is no such region, a pipe end included, and the kind SYS_HANDLE_RECV answered had no reader fstat did not serve. That is why the m3 mutant, which made recv_handles_exact ignore the kinds, survived on the T14. Every pipe site uses its ends generically, where a wrong kind answers PermissionDenied or NotSupported and never WrongType. Deleted: KObjectRef::object_kind, the Received write and type (handle_recv takes &mut [RawHandle] again), the kinds in recv_handles and recv_handles_exact and every caller's kind list, DATA_KINDS, STREAM_OPENED_KINDS, and the supervisor's Received batch. netstack's pipes.rs paragraph, Sockets::answered's doc and netstack_socket_churn are main's again, because main's text is true again. recv_handles_exact stays folded into the method, count-only. ObjectKind stays, for the census alone. The kernel's one new answer is fstat's DeviceMemory. issues/a-received-handle-has-no-knowable-type.md is restored and not closed: a received Process, Namespace, Connector, Inbox or SysCap answers fstat Unknown and an Acceptor answers Pipe, so a typed use of one is still a WrongType the sender can cause. It is narrowed to that, with an exit. shm.rs's contract now says why lent judges before it maps: mapping a handle that names no region ends the caller. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…design as amended Stage 0 is done: the scout's reading on #863 (S = 0.192 ms per MB, the copies 73% of the hold) joins the evidence. The owner's acceptance and its constraint, that the kernel gains only what it alone can answer, is the design line every stage keeps (#863 (comment)). Stage 1 is #867, narrowed by the orchestrator's ruling there (#867 (comment)): the kernel's one new answer is fstat's DeviceMemory, SYS_HANDLE_RECV answers no kind and SYS_SHM_MAP no length, and a receiver judges a lent region through fstat before it maps it. It closes the lent-window issue alone; issues/a-received-handle-has-no-knowable-type.md stays open, narrowed by #867. Stage 2 is #878. The exit read pipe_copy_bytes "per MB downloaded", a unit the row does not print: the row is per byte downloaded, 2.00 today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
|
T14 results at
The green boots' kernel logs: shared-debug, testcases, testcases-watchdog. Every file is masked and committed whole at evidence commit |
|
Review of #867 at Net lines (
Round 2
Checked
BLOCKER
NOTE
SEND BACK |
`toyos-desktop`'s `budget.rs` kept its own `PAGE_2M` for what the kernel rounds a shared region up to. That fact is now declared once in the ABI as `SHM_PAGE`, which the kernel const-asserts equals its page size, so the private copy is deleted and `window_bytes` and its test read the ABI's. Review round 3's BLOCKER on #867. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…suite, image build and the composited screen test Round 4: the compositor's window budget reads SHM_PAGE, and origin/main is merged. Each log whole, the account's home and checkout paths and the per-user temporary directory masked.
|
Review of #867 at Net lines (
Since
Round 3
The implementer's T14 argument: accepted. The c85fbc7 T14 reading and guest suite stand for this head, because a re-run would boot the same binaries:
Checked, not raised
BLOCKER NOTE LAND |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Stage 1 landed as #867 and stage 2 as #878. #867 deleted issues/a-file-server-maps-a-lent-window-at-the-size-it-expects.md, so stage 1 no longer cites it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Brings in #867, #878, #879, #880, #882 and #885. The one file both sides touch, kernel/src/object/ops.rs, merges without conflict: main's hunk is in `fstat` and this branch's are in `close` and `pipe_end_watch`. The branch's diff against main after the merge is its diff against the old base, 177e72a, line for line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
What lands
Stage 1 of the session-region design on #863, as the orchestrator narrowed it on this pull request (#867 (comment)) under the owner's standing constraint that the kernel gains only what it alone can answer. The kernel's one new answer is
SYS_FSTAT's: a shared-memory region that is not memory the kernel allocated to it answers the newFileType::DeviceMemory. A receiver judges a region a peer lent byfstatbefore it maps it.SYS_HANDLE_RECVandSYS_SHM_MAParemain's. This branch does no netstack session work.Closes
issues/a-file-server-maps-a-lent-window-at-the-size-it-expects.md: fileserver refuses by name a hello whose window is notWINDOW_BYTESof memory the kernel allocated, andlent_device_memory_refusedlends one a device claim answered. Nothing outsideissues/cites it.issues/a-received-handle-has-no-knowable-type.mdstays open, narrowed. The ruling named what still holds as a receivedProcess,Acceptor,NamespaceorConnectionused typed. The issue as this branch writes it takes its list fromfstat's own match instead (kernel/src/object/ops.rs:669-682), so it departs from the ruling's words in two places:Connectionas settled, because aConnectionis the only kind that answersSocket;Connector,InboxandSysCap, which answerUnknown, as aProcessand aNamespacedo.An
AcceptoranswersPipe, as a pipe end does. So a typed use of a receivedProcess,Namespace,Connector,Inbox,SysCaporAcceptoris still aWrongTypeits sender can cause. Its one reach today is theProcessthattoyos::launchtakes from the supervisor.Decisions
SYS_FSTATanswers the RAM bit as a file type. ASharedMemhandle answersFileType::SharedMemorywhenSharedMemObject::ram()says the kernel allocated its pages and maps them write-back. It answersFileType::DeviceMemory = 13otherwise, as for a BAR.ram()is the verdictpcidev::dma_mapalready lends a device by. It is a file type, not a newStatfield, becausefile_typealready answers "what is this" and already splits one object kind for pipes (TtyorPipe), and it costs no new word. libc'sfstatmaps both to0o644through its existing_arm.SharedMemory::lent(handle, bytes)judges, then maps. It readsfstat, which resolves a handle of any kind and needs no right, and refuses anything butSharedMemoryexactlybytesrounded up to wholeSHM_PAGEs. Only then does it map. A handle that is no RAM region answers another type (a pipe endPipe, a connectionSocket, a processUnknown). It is refused asNotRambeforeSYS_SHM_MAPcould end the caller withWrongType. ItsLenterror names the refusal:Kernel,NotRamorLength.SharedMemory::adoptkeepsmain's behaviour under a narrower doc: it takes a region the kernel itself handed this process, such as a device's window or a DMA grant.SHM_PAGEis the one declaration of the shared-memory unit.SHM_PAGE(2 MiB) names in the ABI whatSYS_SHM_CREATErounds to, and the kernel const-asserts that it equalsPAGE_2M. The compositor's window budget (toyos-desktop'swindow_bytesand its test) reads it, and its privatePAGE_2Mis deleted (review round 3's BLOCKER).fstatalready serves every reader the kind answer had. Every pipe site uses its ends generically: a read or write of the wrong kind answersPermissionDenied, and a watch answersNotSupported, neverWrongType. SoReceived,object_kindand the kinds inrecv_handles_exactare gone, and netstack, the supervisor andpipes.rsaremain's.Connection::recv_handles_exactis count-only, as onmain. The free function of the same name had one caller, the method, and is folded into it.ObjectKind, checked at compile time.CENSUS_KINDtakes anObjectKindword.kobject!'scensus::live(kind)matches eachObjectKindto its counter by name, so a kernel object with no ABI kind fails to compile.OBJECT_KINDS's strings and the name-equality assertionCENSUS_KINDran on every call.ObjectKind::ALLto word order.Census::kindtakes anObjectKind, so a misspelled name no longer exists to panic on.lent:WINDOW_BYTESlong, and not memory the kernel allocated.Region::lent. The arm afterdma_mapthat took an oversized region back is deleted, becauselentrefuses that region beforedma_mapis reached.CreateError::Lent), and the audio client's slot ring (AudioError::Lent).toyos::log's stdio ring and the supervisor's launch aremain's code. Each already takes a slot as a ring only whenfstatanswersSharedMemory. A region that is not RAM is now a plain handle to both: stdio never maps it, and the supervisor passes it to the child rather than putting its own ring in its place.~/.cargo/git/checkouts/and nothing inrust/librarythat namestoyosuses a name this branch removes or reshapes (fork-search.sh, its output). The round-4 change renames nothing.High-risk checks (ABI, a security boundary)
Each tier has one test making one claim:
lent_window_refused, a row on the T14's shared boot.fs:/homewhose window is a pipe end, or a region twiceWINDOW_BYTES, is let go unanswered, and the next good hello is answered with the same volume.lent, so this case holds the claim that a refused handle is never mapped (map-before-judge, below).lent_device_memory_refused, a QEMU machine test (MACHINE_TESTS, andRUST_SKIPoff the shared boot). A hello whose window is the BAR of QEMU's virtio NIC is let go unanswered, and the next is answered by the same process. The harness asserts fileserver's lineits window is not memory the kernel allocated.sdk/toyos/src/shm.rsholdjudge's verdict: RAM of the right length is taken; not-RAM, longer, shorter and an overflowing length are refused.Negative control. The whole change, reverted onto 177e72a, keeping only
lent_window_refused.rs,lent_device_memory_refused.rs,lent_hello.rsandtests/toyos.rs(patch, its 19 files):lent_device_memory_refused: EXIT=1 (log),a hello lending a device's BAR answered Ok(3): on the base, fileserver serves the BAR.lent_window_refused, built at temporary commit 69af6843b: judge EXIT=1 (judge). The 2× region was answeredOk(11), and the test panicked on "a hostile hello was not let go unanswered" (kernel log).Mutants. Each was applied with
git apply --checkthengit applyon c85fbc7, run, and reverted in the same script (controls.sh, its output; controls2.sh, its output). Round 2's m3 and m5 mutated the kind check and the kind answer, which are deleted.lenttakesDeviceMemoryOk(3))a_region_that_is_not_ram_is_refused_whatever_its_lengthlenttakes a longer regiona_region_longer_than_the_protocol_says_is_refused; the metal 2× case is not run for itfstatanswers every regionSharedMemoryOk(3)). Its first run was invalidated by a host suspend, EXIT=2 (log), andcontrols2.shran it again.lentmaps, then judges (review round 2's named control)shm_mapwas a handle fault: fileserver pid 3 endedcode=139and was started again as pid 11. Both hellos answeredErr(Gone), and the test panicked on "the server that let go is not the one that answers", 11 against 3 (kernel log).The scripts' restores, and the third temporary commit. Each script stages a T14 arm by committing it to a temporary commit, then resets to c85fbc7 and prints HEAD and
git status --porcelain.git reset --hardfailed with it ("No space left on device"). So controls.out ends with HEAD left at that third temporary commit, 889d1d1c, and an empty porcelain.git branch -a --containsprints nothing for each.Independent oracle. The PCI specification's definition of a memory BAR: an address range the function decodes, not system memory. The test expects the BAR to be refused because of that definition, not because of the kernel's
ram(). m4 and m1 show the test fails when the kernel or the SDK disagrees with it. The length rule is the ABI's ownSHM_PAGErounding.Why each tier.
SYS_SHM_CREATE, which the T14 has, so they are a row on the metal tier.bar_map_againrecords, and the shared boot cannot claim it.The T14, at c85fbc7
Each image's sha256 matched metal-images.sha256 before its flash. Each arm was staged from a committed tree with
cargo test --test toyos-build -- --metal --metal-readback <dir> <words>(EXIT=2, staged), and was judged only on the boots that ran, with the words it was staged with.boot:testcasesandboot:shared-debug(3 boots)Ok(11): "a hostile hello was not let go unanswered" (judge, kernel)Err(Gone), but "the server that let go is not the one that answers": the pipe end's map ended fileserver (judge, kernel)The green boots' kernel logs are shared-debug, testcases and testcases-watchdog.
===TEST_ENDlines areexit=0, among themabuse_shared_grant,lent_window_refusedandmutual_kill.exit=0, among themhandle_lifetime,shm_release_reclaimsandspawn_lands_claimed.The green arm covers both shared boots, because every binary that opens a file goes through fileserver's changed hello. A first staging of map-before-judge stopped on the full disk (above).
No T14 re-run is owed at 1423e13. Since c85fbc7, the branch has changed in two ways:
system/compositor/desktop/src/budget.rs, the round-4 fix;origin/main(528b9a6), which bringsissues/and.claude/agents/files and no code.Of the root image's
bin/, onlybin/compositor's size moves, 1029248 → 1029256 bytes (both build logs). The compositor is spawned on none of the three green T14 boots: their kernel logs have nocompositorline. Andwindow_bytesanswers the same for every screen:SHM_PAGEis2 << 20, the deleted const's2 * 1024 * 1024, anda_window_costs_whole_pagesholds the same values at both heads.Gates
At 1423e13 (this head):
cargo run -- --ci host: EXIT=0 (exit),73 step(s), all green(log). It includesbudget::tests::a_window_costs_whole_pages ... ok(L1743).cargo run -- --build-only: EXIT=0 (exit, log).cargo test --test toyos-build -- screen_fatal_halt_composited: EXIT=0 (exit, log). Its boot (tests/images/panelcase.toml) starts the compositor as a service, so it runs the changedwindow_bytes.At c85fbc7 (the change before the round-4 fix and the merge):
cargo test(the guest suite): EXIT=0 (exit),62 passed, 62 total, withlent_device_memory_refusedamong them (log). It bootsMACHINE_TESTSandSCREEN_TESTSonly. The shared-boot binaries this change touches (abuse_shared_grant,handle_lifetime,mutual_kill,shm_release_reclaims, and every one that opens a file) are in the T14 green arm above.cargo test --test toyos-build -- lent_device_memory_refused: EXIT=0 (log).cargo run -- --ci host: EXIT=0 (log).Net lines (
git diff --shortstat origin/main...1423e1304): 26 files, +422 −192.shm.rs's host tests. The kernel's share is +15 −20.What I am unsure of
fstat's match, not the ruling's words:Connectionout, andConnector,InboxandSysCapin (above).DeviceMemoryfollowsram(), not "is this RAM". A region whose pages the kernel does not own, such as firmware's framebuffer (Region::pagesisNone), answersDeviceMemorywhatever memory backs it. That is the verdictdma_mapalready uses. Every region a caller oflenttakes at this head is one its peer made withSharedMemory::create(compositor, soundserver, supervisor, the test clients).🤖 Generated with Claude Code
https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy