diff --git a/docs/bounds-track-plan.md b/docs/bounds-track-plan.md index 2e37820..ce0f813 100644 --- a/docs/bounds-track-plan.md +++ b/docs/bounds-track-plan.md @@ -137,12 +137,16 @@ giver, and its comment says so rather than implying a protection it is not provi | 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 — **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 | +| E6 | Delete the delegated-set scaffolding — every driver is delegated now — **done** | 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. +**Run 3 complete.** Suite 118/118. Every driver receives its hardware; a grant is a +loan that returns to its lender when the borrower dies; nothing firmware-discovered is +left unheld except the framebuffer, which the compositor owns. + ### E4's scope, stated It covers the **boot snapshot**. A device *reported* later and matched to no driver diff --git a/system/services/device-manager/device-manager.zig b/system/services/device-manager/device-manager.zig index daf0c19..5c04e0c 100644 --- a/system/services/device-manager/device-manager.zig +++ b/system/services/device-manager/device-manager.zig @@ -244,35 +244,6 @@ fn alreadySupervised(name: []const u8) bool { return false; } -/// Drivers that receive their device from the manager rather than claiming it -/// themselves. Scaffolding for the conversion, not a permanent concept: it exists so -/// each driver can move across one at a time with the suite green throughout, and it -/// disappears at D6 when `device_claim` stops being a way to acquire a device at all -/// (docs/bounds-track-plan.md, Run 2). -/// -/// `usb-xhci-bus` is first because it was the first driver to conform to `hello` -/// (device-manager.md, M18.1), so it is the one whose handshake is best proven. -const delegated_drivers = [_][]const u8{ - "usb-xhci-bus", - "pci-bus", - "virtio-gpu", - "ps2-bus", - "discovery", -}; - -/// Matched on the **last path component**, because a driver reaches this table under -/// two different spellings: the boot-snapshot match records the bare `pci-bus`, while a -/// devices.csv match records the full `/system/drivers/pci-bus`. Comparing whole -/// strings silently missed the bare form — pci-bus was left neither claiming nor -/// delegated, and died on `ECAM mmio_map failed`. -fn isDelegated(name: []const u8) bool { - const leaf = if (std.mem.lastIndexOfScalar(u8, name, '/')) |slash| name[slash + 1 ..] else name; - for (delegated_drivers) |candidate| { - if (std.mem.eql(u8, leaf, candidate)) return true; - } - return false; -} - /// Record a driver in the table and spawn its first instance. fn addDriver(name: []const u8, device_id: u64, speaks_protocol: bool) void { for (&drivers) |*driver| { @@ -298,7 +269,7 @@ fn spawnDriver(driver: *Driver) void { // rather than advisory. Re-claiming across a restart is expected to say // AlreadyClaimed once the manager already holds it, and that is fine: it means the // device never left our hands while the driver was dead. - if (isDelegated(driver.name()) and driver.device_id != device_manager_protocol.no_device) { + if (driver.device_id != device_manager_protocol.no_device) { device.claim(driver.device_id) catch |e| switch (e) { error.AlreadyClaimed => {}, // ours already, from a previous spawn of this driver else => { @@ -320,7 +291,7 @@ fn spawnDriver(driver: *Driver) void { // A transfer *after* spawning would leave a window in which the child is running // without its hardware — closed on one machine, open on another // (docs/bounds-track-plan.md, "the grant rides system_spawn"). - const give = if (isDelegated(driver.name())) driver.device_id else device_manager_protocol.no_device; + const give = driver.device_id; if (give != device_manager_protocol.no_device) std.log.info("delegated device {d} to {s}", .{ give, driver.name() }); const child = process.spawnSupervisedWithDevice(driver.name(), arguments[0..argument_count], manager_endpoint, give) orelse { diff --git a/test/system/services/crash-test/crash-test.zig b/test/system/services/crash-test/crash-test.zig index b5c262a..5806220 100644 --- a/test/system/services/crash-test/crash-test.zig +++ b/test/system/services/crash-test/crash-test.zig @@ -19,13 +19,15 @@ pub fn main(init: process.Init) void { const argument = init.arguments.get(1) orelse return; // bare: stay silent const assigned = std.fmt.parseInt(u64, argument, 10) catch return; - // The respawn only reaches this line because the kernel released the - // previous instance's claim at death. A failed claim exits cleanly — the - // manager reads "meant to stop" and the scenario fails loudly by silence. - device.claim(assigned) catch { - _ = logging.write("crash-test: claim failed\n"); - return; - }; + // The device arrived with the spawn — this fixture is delegated its hardware like + // any other driver, so it holds `assigned` before its first instruction and has + // nothing to claim (docs/os-development/device-authority.md). + // + // The property this scenario checks is unchanged, only its mechanism: a respawned + // instance still gets the device its predecessor held. It used to arrive because + // the kernel released the dead instance's claim and this one re-took it, racing + // anyone else who wanted it; now the device reverts to the manager on death and is + // handed to the replacement, which is the same guarantee without the race. var manager: ?ipc.Handle = null; var tries: u32 = 0;