diff --git a/issues/a-claims-bar-asked-for-again-after-its-handle-closed-panics-the-kernel.md b/issues/a-claims-bar-asked-for-again-after-its-handle-closed-panics-the-kernel.md new file mode 100644 index 00000000000..15fc1e352a1 --- /dev/null +++ b/issues/a-claims-bar-asked-for-again-after-its-handle-closed-panics-the-kernel.md @@ -0,0 +1,44 @@ +--- +status: open +kind: defect +opened: 2026-10-08 +--- + +# A claim's BAR asked for again after its handle closed panics the kernel + +`pcidev::bar_object` (`kernel/src/pcidev/mod.rs`) makes one object per memory +BAR and keeps it in the claim's binding, to answer every later +`SYS_DEVICE_BAR_MAP` of that BAR with "the same object every time". The object +it keeps is counted by its handles like any other: when the one handle the +first answer installed closes, `HandleEntry`'s drop +(`kernel/src/object/handle.rs`) retires it. The binding still holds it, so the +next request for that BAR hands the retired object to `ops::install`, and +`HandleEntry::new` asserts against exactly that. The holder of a live claim +takes the machine down with three ordinary calls: ask, close, ask. + +The same object is retired without a close when the first install is refused +for a full handle table: the entry built for it is dropped. Read from the +code, not run. + +**Measured** at `06bbc236d`, on `tests/testcases` under QEMU with the virtio +NIC as the claimed function: `cargo test --test toyos-build -- bar_map_again` +exits 1 on + + PANIC: panicked at src/object/handle.rs:108:9: + a handle to a retired SharedMem (koid 197) + +after the job's line `bar_map_again: BAR 4 asked for and its handle closed; +asking again`. + +No metal row was run: the T14's one function a job can claim is its I219, and +a red here is the bench's machine in a panic. + +**Exit condition**: a BAR asked for again on a live claim, after every handle +to the first answer has gone, is answered with a handle that maps or with a +refusal, and `bar_map_again` is green; the arm where the first install is +refused for room is read by a test as well. + +**Owner**: whoever holds `issues/every-driver-is-still-in-the-kernel.md`. + +**Its test is deleted**: `a86cafa06` took `bar_map_again` out, and +`git revert a86cafa06` brings it back. diff --git a/issues/a-pci-call-in-flight-names-its-function-by-a-slot-the-next-claim-reuses.md b/issues/a-pci-call-in-flight-names-its-function-by-a-slot-the-next-claim-reuses.md new file mode 100644 index 00000000000..5e8445994aa --- /dev/null +++ b/issues/a-pci-call-in-flight-names-its-function-by-a-slot-the-next-claim-reuses.md @@ -0,0 +1,38 @@ +--- +status: open +kind: defect +opened: 2026-10-08 +--- + +# A PCI call in flight names its function by a slot the next claim reuses + +Every syscall on a PCI claim — the BAR, the DMA grants and mappings, the +config read — resolves its handle to a `pcidev` slot number +(`pci_slot` and the register target, `kernel/src/syscall/device.rs`) and lets +go of the claim before it acts. What +it then acts on is whatever `BOUND[slot]` holds when `with_bound` +(`kernel/src/pcidev/mod.rs`) takes that slot's lock, which checks that the +slot is bound and not faulted, and not whose binding it is. The two steps are +under different locks with nothing held across them. + +A slot is not a name for one claim: the last handle's close releases it, a +function that was reset leaves it `Free`, and `slot::reserve` +(`kernel/pci/src/slot.rs`) gives the first free slot to the next claim, +whichever function and process that is. A call that resolved its slot before +its own claim was released therefore runs against the next holder's function, +and answers its caller with that function's object. A claim is authority over +one function for one holder; here it reaches another's. + +**Read from the code, not run.** A call made after the close is refused at +the handle, so only a call already past `pci_slot` is exposed, and a thread +in a syscall holding no lock can be preempted there. A guest cannot order a +release and a second claim inside that interval; the test owes the kernel a +hold at that point, which is an actuator this tree does not have. + +**Exit condition**: a call on a claim acts on that claim's binding or is +refused — its identity is held from the handle to the act, or the binding is +checked against it — and a test that holds a call between the two steps, +across the claim's release and the slot's next claim, sees it refused with +the second holder's function untouched. + +**Owner**: whoever holds `issues/every-driver-is-still-in-the-kernel.md`. diff --git a/issues/netstack-keeps-the-socket-of-a-connection-whose-client-sent-no-close.md b/issues/netstack-keeps-the-socket-of-a-connection-whose-client-sent-no-close.md new file mode 100644 index 00000000000..c67131a84d0 --- /dev/null +++ b/issues/netstack-keeps-the-socket-of-a-connection-whose-client-sent-no-close.md @@ -0,0 +1,41 @@ +--- +status: open +kind: defect +opened: 2026-10-08 +--- + +# netstack keeps the socket of a connection whose client sent no close + +A piped TCP stream has three things in netstack +(`userland/netstack/src/main.rs`): the bridge in `piped_connections`, the +smoltcp socket in the socket set with its two 64 KiB buffers, and the id's +entry in `sockets`. A client's close request removes all three. When a +connection ends any other way — the peer finishes or resets and the client +drops its pipe ends, or the client dies — `bridge_piped` removes the bridge +and nothing else. The socket and its entry stay for the life of the process. + +`max_piped_connections` counts bridges and pending connects, so each ended +connection gives its slot back while its socket stays: the bound netstack +states for what clients can make it hold does not cover this, and every +client holding the `netstack` connector can grow it one connection at a time. +`inspect` reports the entries in `net.sockets.tcp` and has no count that tells +a kept one from a live one. + +**Measured** at `06bbc236d`, on `tests/netcase` under QEMU against a host +server that ends each connection at once: +`cargo test --test toyos-build -- netstack_socket_churn` exits 1 on + + netstack_socket_churn: 4 connections ended and let go; netstack held 0 stream(s) before them and holds 4 after + +each of the four read after `net.piped.live` had returned to its reading +before the first. The same run with the close request sent before the drop +exits 0. + +**Exit condition**: a connection netstack lets go leaves no socket and no +table entry, whoever ended it, and `netstack_socket_churn` is green. + +**Owner**: whoever holds `issues/toyos-has-its-own-network-stack.md`. + +**Its test is deleted**: `8dd55d06a` took `netstack_socket_churn` out, with +the `netstack` connector `tests/netcase`'s runner held for it, and +`git revert 8dd55d06a` brings both back.