DF-4415 / psm_reach.c
/* * DF-4415 probe: unprivileged reachability of the atkbdc polling loops. * * sys/dev/misc/kbd/atkbdc.c: on DragonFly /dev/psm0 is created 0666 * (crw-rw-rw-), so ANY local user can issue mouse ioctls that funnel into * the atkbdc controller-layer busy-wait pollers (wait_while_controller_busy * :417, wait_for_aux_ack :545, empty_aux_buffer :894 ...). Each poller burns * DELAY()-busy CPU for up to 70-200ms x KBD_MAXRETRY(3) whenever the * controller is slow; and the *flood* loops below have NO overall timeout: * * empty_kbd_buffer :865-882 `t = wait;` reset on EVERY byte * empty_aux_buffer :905-922 `t = wait;` reset on EVERY byte * read_kbd_data_no_wait :799-804 while AUX-full: keep reading * read_aux_data_no_wait :838-843 while KBD-full: keep reading * * empty_both_buffers (:934-978) got an explicit `waited` cap for exactly * this scenario ("Intel/IBM blades ... will hang in this procedure"), the * four siblings above did not -> an i8042/EC (or VM host device model) that * keeps streaming data wedges the kernel thread forever. * * This probe demonstrates the unprivileged entry path; the unbounded loop * itself needs a flooding controller, which guest userspace cannot inject * (no /dev/io access for non-root). */ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mouse.h> static double now_s(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } int main(void) { mousemode_t mode, omode; mousehw_t info; int fd, i, ok_get = 0, ok_set = 0; double t0, dt, worst = 0; printf("uid=%d euid=%d\n", getuid(), geteuid()); fd = open("/dev/psm0", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("open(/dev/psm0): %s\n", strerror(errno)); return 2; } printf("open(/dev/psm0): OK (world-writable by default on DFly)\n"); if (ioctl(fd, MOUSE_GETHWINFO, &info) == 0) { ok_get = 1; printf("MOUSE_GETINFO: OK (buttons=%d iftype=%d type=%d " "hwid=%d) -- reached the atkbdc layer as uid %d\n", info.buttons, info.iftype, info.type, info.hwid, getuid()); } else { printf("MOUSE_GETINFO: %s\n", strerror(errno)); } if (ioctl(fd, MOUSE_GETMODE, &omode) == 0) { /* re-set the SAME mode: drives psm -> send_aux_command() -> * write_aux_command -> wait_while_controller_busy pollers */ mode = omode; for (i = 0; i < 20; i++) { t0 = now_s(); if (ioctl(fd, MOUSE_SETMODE, &mode) == 0) ok_set++; dt = now_s() - t0; if (dt > worst) worst = dt; } printf("MOUSE_SETMODE x20: %d ok, worst latency %.6fs " "(each call runs the atkbdc busy-wait poll chain)\n", ok_set, worst); } else { printf("MOUSE_GETMODE: %s\n", strerror(errno)); } printf("unbounded-flood loops (empty_kbd_buffer/empty_aux_buffer/" "read_*_no_wait) NOT exercisable from guest userspace: they need " "an i8042/EC data flood (hardware / VM-host injection)\n"); close(fd); return (ok_get || ok_set) ? 0 : 1; } |