//! 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. //! //! **What this fixture cannot yet claim.** `device_claim` is still //! first-come-first-served at this point in the run — that is the hole D6 //! closes. So the claim half of the invariant ("a process holds what it was //! handed and cannot name its way into holding more") is deliberately NOT //! asserted here; it is added to this fixture at D6, when it becomes true. //! Asserting it now would mean writing a test that documents the bug. 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; 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); if (failures == 0) { line("device-authority: ok ({d} devices, none of them mine)\n", .{seen}); } else { line("device-authority: 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(); }