device-manager: hold the seeded hardware so none is left lying around

A device nobody holds can be claimed by anyone, so the manager now takes
every firmware-discovered device that carries mappable resources, whether or
not a driver wants it. The real gap was the HPET: an MMIO window, an IRQ, no
user-space driver, and there for the taking. Held by the manager it is
inert; unheld it was a way into physical memory.

Two deliberate exclusions. The loader's framebuffer, which the compositor
claims and which the manager must not take because it starts first. And
anything with no resources, which grants nothing worth holding.

Scope is the boot snapshot. A device reported later and matched to no driver
stays claimable — pci-cap-test and iommu-fault-test both reach an unmatched
NIC that way, so narrowing it is a separate change with those fixtures in
scope. Recorded in the plan rather than left implied.

The attacker fixture gains the assertion deferred since D2: after the system
settles, nothing with resources may be taken.

That assertion defeated itself twice before it worked, and both failures are
worth remembering. First it swept at 0.029 while the manager did not bind
its protocol until 0.047, so it reported a hole that closed a millisecond
later. The retry loop that "fixed" that was worse: the first pass TAKES the
device, so the second finds it unavailable because this process now holds
it, and concludes all is well — it passed with the manager's claiming
removed entirely. It now settles once and sweeps once, and fails when the
claiming is removed.

Suite 118/118.
This commit is contained in:
Daniel Samson
2026-08-08 22:42:42 +01:00
parent ba195fa0a2
commit df9c1ed827
5 changed files with 64 additions and 3 deletions
+13 -2
View File
@@ -135,14 +135,25 @@ giver, and its comment says so rather than implying a protection it is not provi
| E1 | Record a giver per device; `device_transfer` and the spawn grant set it — **done** |
| E2 | On task death a device reverts to its giver if alive, else its claim clears — **done** |
| E3 | `device_claim` refuses a device that has a giver — **done**, but unreachable: E2 already closed the window |
| E4 | The manager claims every resource-bearing device at boot, so nothing is left takeable |
| E5 | The attacker fixture gains the claim half it has been waiting for since D2 |
| E4 | The manager claims every resource-bearing device at boot, so nothing is left takeable — **done** (boot snapshot only; see below) |
| E5 | The attacker fixture gains the claim half it has been waiting for since D2 — **done with E4** |
| E6 | Delete the delegated-set scaffolding — every driver is delegated now |
Ordering: E1 alone changes no behaviour. E2 must precede E3, or a restart cannot
re-acquire. E4 must precede E5, or the attacker will find takeable devices and the
assertion will be wrong about why. E6 is cleanup.
### E4's scope, stated
It covers the **boot snapshot**. A device *reported* later and matched to no driver
stays claimable — `pci-cap-test` and `iommu-fault-test` both rely on that to reach an
unmatched NIC. Narrowing it further is a separate change with those fixtures in scope.
The real gap it closed was the **HPET**: an MMIO window, an IRQ, no user-space driver,
and claimable by anyone. Excluded on purpose: the loader's framebuffer (the manager
starts before display, so taking it would break the boot screen) and anything with no
resources, which grants nothing.
### Not in this run, and not blocking it
**Zero-resource devices stay in the kernel.** Moving them out needs an answer to who