//! device-authority-test — the attacker the device suite never had. //! //! The audit behind [docs/fixed-bounds-audit.md] found six real defects that a //! fully green suite had missed, and the reason was structural: *the suite //! contains no attacker*. Every device case asserts that a driver handed its //! own hardware can drive it. None asks what a process that was handed //! **nothing** can do. //! //! This binary is that process. It is spawned with no device, holds no device, //! and asserts what it therefore cannot do //! ([docs/os-development/device-authority.md]): //! //! 1. **A positive control first.** `device_enumerate` works from here, so //! the refusals below are decisions rather than a syscall path that is //! simply broken for this process. Without this, "everything failed" would //! read identically to "the assertions are meaningless". //! 2. **It cannot give away a device it does not hold** — not one another //! task holds, and not a free one either. The kernel's whole rule is *you //! may give away what you hold*, so the state of the device is irrelevant: //! a process holding nothing can transfer nothing. That is asserted across //! several ids precisely so it cannot pass by accident of which device //! happened to be free at boot. //! 3. **A device that does not exist is refused differently** — `NoSuchDevice` //! rather than `NotHeld`. A refusal that cannot say which rule refused it //! is what cost a debugging session on the Ryzen, so the distinction is //! part of the contract and is tested as such. //! //! **Why there is no "cannot take a delegated device" assertion here.** The //! hole this fixture was written for is closed, but not by a refusal it could //! observe. A device that was given to someone is *held*, so an attempt to //! take it is refused as `AlreadyClaimed` — the same answer as before. What //! changed is what happens when the holder dies: the device returns to //! whoever lent it instead of becoming free, so the window in which a //! stranger could take it no longer exists. There is no moment to catch. const std = @import("std"); const device = @import("driver"); const logging = @import("logging"); const process = @import("process"); fn line(comptime format: []const u8, arguments: anytype) void { var buffer: [160]u8 = undefined; _ = logging.write(std.fmt.bufPrint(&buffer, format, arguments) catch return); } var failures: usize = 0; var process_table: [64]process.ProcessDescriptor = undefined; fn check(name: []const u8, ok: bool) void { if (!ok) failures += 1; line("device-authority: {s} {s}\n", .{ if (ok) "ok" else "FAIL", name }); } fn run() void { // 1. The positive control: this process can reach the device syscalls at all. var table: [64]device.DeviceDescriptor = undefined; const total = device.enumerate(&table); check("enumerate works from an unprivileged process", total > 0); const seen = @min(total, table.len); // 2. Holding nothing, it can give nothing away — whatever the device's state. // Every id the machine actually has, so this cannot pass by luck. var refused: usize = 0; var wrong_reason: usize = 0; for (table[0..seen]) |descriptor| { device.transfer(descriptor.id, process.taskId()) catch |e| { refused += 1; if (e != error.NotHeld) wrong_reason += 1; continue; }; } check("every transfer by a non-holder is refused", refused == seen); check("each refusal says NotHeld, not something vaguer", wrong_reason == 0); // 3. A device that does not exist is a different refusal, and says so. const absent = if (device.transfer(0xFFFF_FFFF, process.taskId())) |_| false else |e| e == error.NoSuchDevice; check("a device that does not exist is refused as absent", absent); // 4. **The spawn is not a second way in.** A device now rides system_spawn, which // would be a fine back door if the kernel checked ownership any less carefully // there than it does in transfer: spawn a child, name someone else's device, and // the child holds hardware nobody gave it. The refusal must happen before the // child exists, so nothing is left running either. if (seen != 0) { const before = process.processes(&process_table); const spawned = process.spawnSupervisedWithDevice("/test/system/services/device-authority-test", &.{}, null, table[0].id); check("spawning with a device the caller does not hold is refused", spawned == null); check("and no child was left behind by the refusal", process.processes(&process_table) == before); } if (failures == 0) { line("device-authority: VERDICT ok ({d} devices, none of them mine)\n", .{seen}); } else { line("device-authority: VERDICT FAILED {d} assertion(s)\n", .{failures}); } } pub fn main(startup: process.Init) void { const role = startup.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent if (std.mem.eql(u8, role, "run")) run(); }