Repository navigation
A receive reports what each handle names, and a typed handle is made of a received one only where the kernel said so - #1239
Conversation
Review of #1239 at
|
… is known `PciDev`, `AcpiDev`, `FramebufferDev` and `UartDev` declared `KINDS = [Device]`, so `ReceivedHandle::typed` made any device claim one of them, and a claim of another class then ended the receiver at its first class call. A claim's kind does not say its class. `FromHandle` keeps only the unsafe constructor. Its Safety text is now what the kernel holds a type's calls to: the object takes every call of the type, which for a class type means a claim of that class. `KINDS` moves to `OfKind`, which only types that any object of their kinds serves implement, and `typed` requires it. The four class types implement `FromHandle` and `ClaimType`, a marker for what `SysCap` mints. It replaces the const assert that a minted type is a device's, so `SysCap::claim` and the guest tests' `function::claim` take the class from the call that names it. A received claim can become only a `Device`. From the review's suggestions: - `ReceivedHandle::kind`, which had no caller, is gone. - `ask_export` types netstack's exported end before it hands it on. - Both `adopt_exported`s take a `Connection` and are safe. The one `unsafe` is where `fd` takes an endowed export, whose kind is the parent's word. `ObjectKind`'s definition now says that a kind which goes leaves its word unused for good. A new record names the connection calls that still take any `AsHandle`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ad7d762 to
d8090d4
Compare
Review round 2 of #1239 at
|
…aimed for Two SAFETY comments still argued that a claim's kind decides its class type. That is the reasoning round 1 refuted. They now give the class instead. - The supervisor's `translated` makes a `PciDev` of a claim minted for its `function`, a PCI function. - `SysCap::mint` makes `T` of a claim of the class the request names. `SysCap::mint`'s comment, and `ClaimType`'s doc, now also say that pairing a class type with the class a call claims is the caller's to keep: the bound does not hold it. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator: verified 15fa1e5 against follow-up r2. |
…le the benches are down The benches are down, so metal is no longer a landing condition. What only metal shows of this change is recorded as owed in `issues/a-connection-call-on-a-bare-handle-of-another-kind-ends-its-caller.md`: the T14's `testcases`, `testcases-watchdog`, `shared-debug` and `proctreecase` boots of main. They carry the shared-boot members `messages_are_whole`, `mutual_kill`, `moved_rights`, `served_file_offset` and `process_tree`, which no guest row runs. A record edit only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Orchestrator: landing under the benches-down rule. Review r2 text fixes verified earlier; head 5773d0d is 15fa1e5 plus one record (verified). Guest run 43092098455 at 15fa1e5: success. The PR's own shared-boot members ( |
…of a received one only where the kernel said so
`SYS_MESSAGE_RECV` answered handle numbers and a count. What a handle named
was the sender's word, a typed lookup of the wrong kind ends its caller, and
`SYS_FSTAT` settles two kinds of twenty: so whoever could send a process a
handle could end it. Read from the code, the reach was `toyos::launch`'s
process handle and every bell a client takes from its server (audio, net
streams and sockets, the link driver, the disk session).
The receive now writes `[ReceivedEntry { handle, kind }]`, the kind an
`ObjectKind` word read in the hold that installs the handle, so there is no
second call to forget and no window. `ObjectKind` becomes a contract: a
word is never renumbered or reused. The pair carries the kind alone.
In the SDK a batch yields `ReceivedHandle`, and `ReceivedHandle::typed::<T>()`
is the one way to a typed wrapper: refused, and the handle closed, unless the
kernel reported one of `T::KINDS`, a word this SDK does not know included.
The unchecked constructors are gone (`Connection::received`,
`Namespace::received`, `From<OwnedHandle>` for `Pipe`, `Waker` and `Bell`),
and `FromHandle::from_handle` is `unsafe`, its contract the kind: an
endowment is its parent's word, a minted claim the kernel's, and a duplicate
of a typed handle its holder's. `SysCap::claim` checks at compile time that
it mints a device's type. std's `adopt_namespace` is `unsafe` for the same
reason. A received handle still becomes a bare `OwnedHandle` to send on,
endow or map: `SharedMemory::lent` keeps `SYS_FSTAT`, which also answers
whether a region is RAM and how long, and `Paged::received` its own probe.
The supervisor refuses by name a launch whose extras, place or job are not a
connector, a process and a job, and a session's place that is no process,
where it left both to the kernel's `get_sent`.
`messages_are_whole` gains the arm the record's exit asks for: eight handles
of eight kinds arrive each beside its kind, and a pipe end taken as a
process is refused by name while the receiver lives and takes the next as a
pipe.
Closes `issues/a-received-handle-has-no-knowable-type.md`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… is known `PciDev`, `AcpiDev`, `FramebufferDev` and `UartDev` declared `KINDS = [Device]`, so `ReceivedHandle::typed` made any device claim one of them, and a claim of another class then ended the receiver at its first class call. A claim's kind does not say its class. `FromHandle` keeps only the unsafe constructor. Its Safety text is now what the kernel holds a type's calls to: the object takes every call of the type, which for a class type means a claim of that class. `KINDS` moves to `OfKind`, which only types that any object of their kinds serves implement, and `typed` requires it. The four class types implement `FromHandle` and `ClaimType`, a marker for what `SysCap` mints. It replaces the const assert that a minted type is a device's, so `SysCap::claim` and the guest tests' `function::claim` take the class from the call that names it. A received claim can become only a `Device`. From the review's suggestions: - `ReceivedHandle::kind`, which had no caller, is gone. - `ask_export` types netstack's exported end before it hands it on. - Both `adopt_exported`s take a `Connection` and are safe. The one `unsafe` is where `fd` takes an endowed export, whose kind is the parent's word. `ObjectKind`'s definition now says that a kind which goes leaves its word unused for good. A new record names the connection calls that still take any `AsHandle`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aimed for Two SAFETY comments still argued that a claim's kind decides its class type. That is the reasoning round 1 refuted. They now give the class instead. - The supervisor's `translated` makes a `PciDev` of a claim minted for its `function`, a PCI function. - `SysCap::mint` makes `T` of a claim of the class the request names. `SysCap::mint`'s comment, and `ClaimType`'s doc, now also say that pairing a class type with the class a call claims is the caller's to keep: the bound does not hold it. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…le the benches are down The benches are down, so metal is no longer a landing condition. What only metal shows of this change is recorded as owed in `issues/a-connection-call-on-a-bare-handle-of-another-kind-ends-its-caller.md`: the T14's `testcases`, `testcases-watchdog`, `shared-debug` and `proctreecase` boots of main. They carry the shared-boot members `messages_are_whole`, `mutual_kill`, `moved_rights`, `served_file_offset` and `process_tree`, which no guest row runs. A record edit only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5773d0d to
4512695
Compare
Rebase review of #1239 at
|
Problem
SYS_MESSAGE_RECVanswered handle numbers and a count. What a received handle named was only the sender's word, a typed call on a handle of the wrong kind ends the caller with a handle fault, andSYS_FSTATtells apart only a region and a connection. So any peer that could send a process a handle could end that process. Reading the code shows the reach:toyos::launch's process handle, and every bell a client takes from its server (audio, net streams and sockets, the link driver, the disk session).Closes
issues/a-received-handle-has-no-knowable-type.md.Resulting behaviour
[ReceivedEntry { handle, kind }]. The kind is anObjectKindword read during the same hold that installs the handle, so the receiver needs no second call and has no window.ObjectKindbecomes a contract: a word is never renumbered or reused, and a new kind takes the next word. The entry carries the kind and no rights, because a missing right already answersPermissionDeniedand ends nobody.ReceivedHandle.ReceivedHandle::typed::<T>()is the only way to get a typed wrapper from it, and only for aT: OfKind, a type that any object of its kinds serves. If the kernel did not report one ofT::KINDS, it refuses withWrongKindand closes the handle. A kind word this SDK does not know is also refused. A claim's class types (PciDev,AcpiDev,FramebufferDev,UartDev) are notOfKind, because a claim's kind does not say its class: a received claim can become only aDevice.Connection::received,Namespace::received, andFrom<OwnedHandle>forPipe,WakerandBellare deleted.FromHandle::from_handleis nowunsafe. Its contract is that the object takes every call of the type, which for a class type means a claim of that class. An endowment is its parent's word, a minted claim the kernel's, and a duplicate of a typed handle its holder's.SysCap's claims take aClaimType,Deviceor a class type, whose class the claiming call names. std'sadopt_namespaceisunsafefor the same reason.OwnedHandleto send on, endow or map.SharedMemory::lentkeepsSYS_FSTAT, which also says whether a region is RAM and how long it is.Paged::receivedkeeps its own probe.get_sent.architecture/authority.mdstates the rule: a received handle arrives with its kind, and a receiver takes it as a kind only where the kernel reported that kind.Checks
The owner's testing policy applies: only the unit and host tests of the touched crates, clippy on the touched packages, and a whole metal boot for an object-surface change.
All checks below ran at
34cb4c918plus this change's tree, which is the pushed commit's tree. The rebase ontob56693152brought onlyissues/edits. Each log is linked in the evidence below. Two edits came after the image build and before the commit: a reflowedmessage_recvdoc comment, which the ABI checks compiled, andmessages_are_wholereading onlygot.handlesentries.cargo test -p toyos-abi -p toyos-blockserve -p toyos-link -p toyos-sessioncargo test -p toyos -p logkeeper-apia_received_handle_is_typed_only_as_the_kind_the_kernel_reported, and logkeeper-apicargo test --manifest-path system/{diskserver,hda,logkeeper,netstack,session,sshserver}/Cargo.tomlcargo clippyin CI's shapes: the touched host members--all-targets,toyos --all-targets,toyos --lib --features fd-table,toyos-abiwithundocumented_unsafe_blocks, and the x86-64 kernel with default features and withboot-actuators,test-actuatorscargo run -- --build-onlyThe supervisor's host tests are in its nested
lifecyclecrate, which this change does not touch. osbench builds only for x86-64 ToyOS and Linux. I did not run the kernel's host library (kernel/pure), the models or aarch64: the change touches neitherkernel/purenor any arch code.Negative control. With
admitmutated to accept any kind this SDK knows, the SDK test reds:left: Ok(()) right: Err(WrongKind { wanted: [EventWait], got: Some(EventSignal) }), exit 101.Guest. The receive's guest claim lives in
messages_are_whole. Its new arm sends eight handles of eight kinds, checks each kind reported beside its handle, and checks that a pipe end taken as a process is refused by name while the receiver lives.process_treecoverslaunch's typedProcess. Both are shared-boot members on metal, not QEMU rows, so no guest row runs them. With the benches down they are owed on metal (below).QEMU rows that reach the changed receivers, one batch on the guest runner, run 43049964255 at
ad7d762c2: 4 passed, 4 total.At the current head
451269586, rebased onto #1246: guest run 43108314485 covers it at that head. The rows aresession_lifecycle,a_launch_gives_each_unsent_stdio_slot_a_null,netstack_holders_exported_to_children(round 1'sask_exportand the socketadopt_exported),hda_plays_to_the_controller_that_reaches_an_output, andlibc_children(spawn_served_stdout:files::exportand the fileadopt_exported). The result follows when it arrives.session_lifecycle(a desktop session's typed namespaces, its owner's place and its started apps' processes),a_launch_gives_each_unsent_stdio_slot_a_null(the supervisor's typed extras and job),netstack_holders_exported_to_children(exported-end adoption, stream bells), andhda_plays_to_the_controller_that_reaches_an_output(the audio bell, hda's ring).Evidence (
ToyOSOrg/evidencepr-1239at9448c5dc): index, host checks (summary), negative control, build, metal staging and request, guest rows.Owed on metal (the benches are down; metal is no longer a landing condition). This is recorded in
issues/a-connection-call-on-a-bare-handle-of-another-kind-ends-its-caller.md:testcases,testcases-watchdogandshared-debugboots of main, whole, and theproctreecaseboot.messages_are_whole: each handle arrived with its kind, and another kind was refused by name, thenmessages_are_whole: ok.mutual_kill,moved_rightsandserved_file_offsetpass.process_treepasses, witha_pipe_is_no_placerefused by the supervisor by name.The images staged at
ad7d762c2for those boots are deleted, and the request stays as the record of what was asked.Review round 1
d8090d44c): the class types lefttyped, by type.KINDSmoved fromFromHandletoOfKind, whichtypedrequires and the four class types do not implement.ClaimTypemarks whatSysCapmints and replaces the const assert. TheFromHandleSafety text now states the per-type contract.6d0cb3ae9. The deleted record stays deleted.served_file_offset's conflict is resolved by main's spawn lines and byfiles::exportreturning aConnection. That one change is in the rebased commit because the resolution needs it: the descriptor owner already types that end throughtyped, so the test needs nounsafe.ReceivedHandle::kind(no caller) is gone.ask_exporttypes netstack's end. Bothadopt_exporteds take aConnectionand are safe, with the oneunsafewherefdtakes an endowed export, whose kind is the parent's word. The connection calls that take anyAsHandleare recorded inissues/a-connection-call-on-a-bare-handle-of-another-kind-ends-its-caller.md.ObjectKindnumbering (the owner's coordinator's decision): kind words are ABI and never renumbered, and A child's handles are a list its parent names: labels and namespaces leave the kernel #1252 retires word 8 instead of shifting the rest.ObjectKind's definition now also says that a retired word stays unused. Whichever of this PR and A child's handles are a list its parent names: labels and namespaces leave the kernel #1252 lands second keepsFromHandle::from_handleunsafe.admission::Client::take_handlesmust return[ReceivedHandle; N], and its callers must usetyped. Its agent does that, whichever of the two lands second.cargo test -p toyos --lib ipc::and clippy in CI's shapes (toyos --all-targets,toyos --lib --features fd-table,toyos-abi), each exit 0. On the guest side, a staging of one shared member compiled std, libc, thetestcasesimage's programs and every guest binary atd8090d44c, exit 2 (staged). That image builds no compositor. The other callers of the changed bounds compile by inspection: the compositor, console, uart and acpiserver callendow::device, whoseFromHandlebound is unchanged, and metalprobe'scap.claimyieldsFramebufferDev, which is aClaimType. See summary and guest compile.ask_exportgained a refusal of an end that is no connection. Its accepting path holds by inspection. netstack makes that end inExports::connect, where it also makes an accepted stream's end, andacceptalready types that one throughtyped::<Connection>.Review round 2 (
15fa1e54d)translatednow gives the claim's class: a claim minted forfunction, a PCI function.SysCap::mint's SAFETY comment andClaimType's doc now say that pairing a class type with the class a call claims is the caller's to keep, because the bound does not hold it.ClaimTypecarrying its class, so that a mismatchedTis refused at compile time. The calls are sound as they are, and that would widen this PR's surface past the record it closes.Rebase onto #1246 (
06fcb81ea)undeliveredinkernel/src/syscall/ipc.rs. A thread carries its process's handles, and a lookup takes no lock but the table's #1246 moved its lookup fromprocess::with_process_data(|data| … data.handles …)toprocess::with_handles(|handles| …), under the handle table's own lock. This PR changed its parameter to&[ReceivedEntry]and its loop to close each entry's.handle.&[ReceivedEntry]and closes each throughprocess::with_handles. Every other site this PR changes in the kernel auto-merged:recv_nowinstalls under the receiver's handle-table lock, as A thread carries its process's handles, and a lookup takes no lock but the table's #1246 now describes it, and reads each kind in that hold.451269586: the kernel's host tests and its clippy shapes (host library, x86-64 default and withboot-actuators,test-actuators, AArch64), each exit 0 (summary). Guest: run 43108314485 at451269586, with the same five rows. Run 43092098455 at15fa1e54dpassed 5 of 5 (log) and is superseded by the rebase.Unresolved
issues/a-driver-that-lends-its-device-a-handle-a-peer-sent-is-ended-when-it-is-no-region.mdstays open. A region has no typed wrapper to refuse at, and that record's exit is the kernel's.message_recvor the SDK's receive. I searched every fork clone and~/.cargo/git/checkouts/, so no fork pin moves.🤖 Generated with Claude Code