From 5a6ff0561008bf96ec90c482c2ea17d245912bba Mon Sep 17 00:00:00 2001 From: BANANASJIM Date: Sat, 6 Jun 2026 21:01:17 -0700 Subject: [PATCH] fix(hidraw): check ioctl errno in featureReport (HIDIOCSFEATURE failures were silently swallowed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - linux.ioctl returns usize, so the `if (rc < 0)` guard was always false; every HIDIOCSFEATURE failure (ENODEV/EPIPE on unplug, EINVAL, EBADF, ...) returned OK and the device-init handshake in init.zig never saw the error. - Interpret the raw syscall return with linux.E.init(rc) and branch on errno != .SUCCESS, mapping ENODEV/EPIPE to Disconnected and the rest to Io. - posix.errno is unsuitable here: under libc linkage it reads the C errno global, which a raw linux.ioctl syscall never sets, so it reported SUCCESS for a -EBADF return. Test plan: - Added Layer-0 regression "hidraw: featureReport surfaces ioctl errno as error": featureReport over fd=-1 (EBADF) must return WriteError.Io. Falsifiable: against the old `if (rc < 0)` code (libc-linked) it fails with "expected error.Io, found void". - `./scripts/padctl-docker test` → EXIT=0 (1635 tests). - `zig fmt --check src/io/hidraw.zig` clean. refs: audit HIGH --- src/io/hidraw.zig | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/io/hidraw.zig b/src/io/hidraw.zig index 918c67e5..67cd0cde 100644 --- a/src/io/hidraw.zig +++ b/src/io/hidraw.zig @@ -254,8 +254,8 @@ pub const HidrawDevice = struct { if (self.wedge) |w| w.beginWrite(); defer if (self.wedge) |w| w.endWrite(); const rc = linux.ioctl(self.fd, req, @intFromPtr(data.ptr)); - if (rc < 0) { - const errno = posix.errno(rc); + const errno = linux.E.init(rc); + if (errno != .SUCCESS) { if (errno == .NODEV or errno == .PIPE) return DeviceIO.WriteError.Disconnected; return DeviceIO.WriteError.Io; } @@ -572,3 +572,19 @@ test "hidraw: discoverWithRoot — nonexistent root returns NotFound for both nu const err3 = HidrawDevice.discoverWithRoot(allocator, 0x37d7, 0x2401, 0, "/nonexistent_hidraw_xyz"); try std.testing.expectError(error.NotFound, err3); } + +// featureReport must surface HIDIOCSFEATURE failures. An invalid fd makes the +// ioctl return EBADF → WriteError.Io. +// Falsifiability: the old `if (rc < 0)` guard never triggered (linux.ioctl returns +// usize, never < 0), so featureReport returned void on failure and this fails. +test "hidraw: featureReport surfaces ioctl errno as error" { + var dev = HidrawDevice{ + .fd = -1, + .evdev_fds = .{}, + .allocator = std.testing.allocator, + }; + const dev_io = dev.deviceIO(); + + const payload = [_]u8{ 0x01, 0x02, 0x03 }; + try std.testing.expectError(DeviceIO.WriteError.Io, dev_io.featureReport(&payload)); +}