kernel: the device rides system_spawn, so a driver never runs without it
Delegation moves out of onHello and into the spawn itself. The manager holds the hardware and names it in the call that creates the driver; the kernel checks the device is the caller's to give, then hands it over as part of making the child. The reason is the window. A transfer after spawning always leaves an interval in which the child is running and does not yet hold its device. It would close on QEMU every time and open occasionally on a machine with different core counts and timing — the exact failure shape this track exists to delete, and not one worth introducing while removing the others. Fused into the spawn there is no interval: the child does not exist until it holds the device. Ownership is checked BEFORE the child is created, so a refusal leaves nothing running rather than a driver without the hardware it was spawned for. The IOMMU confinement moves with the device, as it does on the transfer path. systemCall6 is added for the sixth argument; r9 was free, and abi gains a no_device sentinel matching the protocol's. No driver had to change to receive a device, which is what makes this better than requiring every driver to hello: ps2-bus keeps its legacy status, and discovery — which has no assignment at all, since it is what produces the device tree — is unaffected. The attacker fixture now tries the spawn as a back door: name someone else's device, and both the spawn and any child must be refused. Verifying that assertion exposed a bug in the fixture itself. The kernel case's pass marker was "device-authority: ok", which matches the FIRST per-assertion line, so its wait loop exited before any failure was printed — the case would have passed with failures in it, and had been able to since D2. The verdict lines now carry a distinct VERDICT prefix, and with the ownership check removed the case genuinely fails. A green test that cannot go red is worse than no test. Suite 118/118.
This commit is contained in:
@@ -306,7 +306,14 @@ fn spawnDriver(driver: *Driver) void {
|
||||
arguments[0] = std.fmt.bufPrint(&id_text, "{d}", .{driver.device_id}) catch return;
|
||||
argument_count = 1;
|
||||
}
|
||||
const child = process.spawnSupervised(driver.name(), arguments[0..argument_count], manager_endpoint) orelse {
|
||||
// The device rides the spawn, so the driver holds it before its first instruction.
|
||||
// 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;
|
||||
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 {
|
||||
std.log.info("failed to spawn {s}", .{driver.name()});
|
||||
driver.state = .failed;
|
||||
return;
|
||||
@@ -467,22 +474,6 @@ fn onHello(_: void, invocation: Invocation(device_manager_protocol.Hello), _: An
|
||||
};
|
||||
driver.state = .running;
|
||||
|
||||
// **Delegation.** The manager holds this driver's device and hands it over here —
|
||||
// what replaces first-come-first-served `device_claim` with policy
|
||||
// (docs/os-development/device-authority.md). `invocation.sender` is the driver's
|
||||
// task id stamped by the kernel, so the manager cannot be lied to about who is
|
||||
// asking, and the transfer is a move: the manager stops holding it.
|
||||
//
|
||||
// Gated on the delegated set so an unconverted driver still claims for itself and
|
||||
// its path is untouched; the set and `device_claim` both go at D6.
|
||||
if (isDelegated(driver.name()) and driver.device_id != device_manager_protocol.no_device) {
|
||||
device.transfer(driver.device_id, invocation.sender) catch |e| {
|
||||
std.log.warn("could not delegate device {d} to {s}: {s}", .{ driver.device_id, driver.name(), @errorName(e) });
|
||||
return -envelope.EPERM;
|
||||
};
|
||||
std.log.info("delegated device {d} to {s}", .{ driver.device_id, driver.name() });
|
||||
}
|
||||
|
||||
std.log.info("hello from {s} (device {d})", .{ driver.name(), invocation.target });
|
||||
// Resilience drill (V6): once, kill the virtio-gpu driver a moment after it hellos, so
|
||||
// the normal restart policy respawns it — the compositor must survive and re-attach.
|
||||
|
||||
Reference in New Issue
Block a user