atkbdc drain/poll loops reset their budget on every received byte with no overall cap β unbounded kernel busy-loop under i8042/AUX data flood (empty_both_buffers got the cap, these four did not)
Summary
empty_kbd_buffer and empty_aux_buffer reset t=wait whenever ANY_BUFFER_FULL is set, and the two read_*_no_wait loops run while the opposite channel's buffer stays full - a controller/EC that keeps presenting data wedges the calling kernel thread indefinitely. The file itself proves the scenario is real: empty_both_buffers carries an explicit waited cap added because 'Intel/IBM blades will hang in this procedure'; the four siblings never got it. LIVE-verified the affected layer is unpriv-reachable: /dev/psm0 is 0666 and uid 1999 drove ioctls through psm -> send_aux_command -> the atkbdc poll chain. Flood needs hostile EC/host device model. Port-the-cap fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4415 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| psm_reach.c | β | 3.0 KB | view raw | |
| build.sh | β | 97 B | view raw | |
| run.sh | β | 118 B | view raw | |
| build.log | β | 486 B | view raw | |
| run.log | β | 486 B | view raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| README.md | β | 1.8 KB | β raw | |
| fix.diff | β | 1.7 KB | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| env.txt | β | 585 B | view raw |
DF-4415 β atkbdc.c unbounded polling loops under i8042/AUX data flood (missing the timeout cap that empty_both_buffers has)
Build
cc -O2 -Wall -o /tmp/psm_reach DF-4415/psm_reach.c
Run (as an unprivileged user)
/tmp/psm_reach
What it demonstrates (reproduced on the audit guest)
/dev/psm0is created world read/write (crw-rw-rw-) β any local user reaches the psm driver and, through it, the atkbdc controller layer (atkbdc0backingpsm0per dmesg).MOUSE_GETHWINFOandMOUSE_SETMODEsucceed for uid 1999 βMOUSE_SETMODEdrivespsm β send_aux_command β write_aux_command β wait_while_controller_busy()etc., i.e. the atkbdc busy-wait pollers, on every call. Worst observed round-trip 7.4 ms with the healthy QEMU i8042 (the pollers only burn their full 70-200 ms Γ 3 retries when the controller is slow/wedged).
What it cannot demonstrate from guest userspace
The genuinely unbounded loops need the controller/EC to keep
presenting data faster than the code drains it (hostile EC, or a VM
host's i8042 device model). Guest userspace has no port-I/O path
(/dev/io is root-only crw-------), so the flood cannot be injected
from inside the guest; those loops are:
empty_kbd_buffer()β sys/dev/misc/kbd/atkbdc.c:865-882 (t = waitreset on every byte)empty_aux_buffer()β atkbdc.c:905-922 (same pattern)read_kbd_data_no_wait()β atkbdc.c:799-804 (while (f == KBDS_AUX_BUFFER_FULL))read_aux_data_no_wait()β atkbdc.c:838-843 (while (f == KBDS_KBD_BUFFER_FULL))
empty_both_buffers() (atkbdc.c:934-978) carries an explicit overall cap
(waited, added for "Intel/IBM blades" that exhibit exactly this
controller-flood hang) β the four siblings above never got the same
treatment.
DF-4415 β VERDICT
status: not_reproduced for the unbounded loop itself (needs controller/EC data flood β hardware injection); the unprivileged reachability of the affected code paths WAS reproduced live
What was reproduced on the guest (DF 6.5-DEVELOPMENT, atkbdc0+psm0, QEMU i8042)
/dev/psm0is world read/write (crw-rw-rw-),/dev/kbd0/dev/kbd1are root-only.- As uid 1999:
open("/dev/psm0")OK;MOUSE_GETHWINFOreturns a real PS/2 mouse (buttons=5, iftype=3, hwid=4 βpsm0: model IntelliMouse Explorer, device ID 4 on atkbdc0); 20ΓMOUSE_SETMODEall succeed, worst latency 0.0074 s. - Every
MOUSE_SETMODEreprograms the aux device throughsend_aux_command()(atkbdc.c:647-670) βwrite_aux_command()(:616) βwrite_controller_command()(:586) βwait_while_controller_busy()(:417-439) andwait_for_aux_ack()(:545-569) β the DELAY()-busy poll chain. On a healthy controller this is single-digit milliseconds; the code burns up to ~70-200 ms Γ KBD_MAXRETRY(3) per command when the controller stalls (TOTALDELAY-bounded β a CPU-burn amplifier for any unprivileged user spamming psm ioctls, but bounded).
The actual defect (not demonstrable from guest userspace)
Four drain loops reset their budget on every received byte and have no overall cap, so a controller that keeps streaming data wedges the kernel thread indefinitely:
empty_kbd_buffer()atkbdc.c:865-882 βt = wait;on each byteempty_aux_buffer()atkbdc.c:905-922 β sameread_kbd_data_no_wait()atkbdc.c:799-804 βwhile (f == KBDS_AUX_BUFFER_FULL)keeps readingread_aux_data_no_wait()atkbdc.c:838-843 β mirror image
The maintainer-acknowledged precedent is in the same file:
empty_both_buffers() atkbdc.c:959-966 has a waited cap with the
comment "Some systems (Intel/IBM blades) do not have keyboard devices and
will hang in this procedure. Time out β¦ to avoid this hang". The four
siblings never received the cap.
Threat model
Malicious/slow EC or i8042 device model (embedded coreboot ECs, VM hosts β the file's own quirk table documents "imperfect i8042 emulation" coreboot ECs at atkbdc.c:112-128), or a controller wedge during unprivileged ioctl storms through /dev/psm0. Impact: permanent kernel busy-loop (thread wedge / CPU starvation) β Low (hardware precondition).
Fix
Port the waited cap to the two empty_*_buffer() functions and bound
the two read_*_no_wait() drain loops. See fix.diff.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix validation not meaningful on this guest: the unbounded behavior needs a controller data flood that guest userspace cannot inject; a patched-kernel build would show no behavioral difference.
fix.diff (waited caps + bounded drains)
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (unpriv reachability reproduced); VERDICT.md (four uncapped loops vs the capped sibling); dmesg in env.txt (atkbdc0/psm0 present)
PoC changes
MOUSE_GETINFO/mouseinfo_t (FreeBSD-isms) -> MOUSE_GETHWINFO/mousehw_t per DFly sys/mouse.h
Verified recommended fix
Port empty_both_buffers' overall 'waited' cap to empty_kbd_buffer/empty_aux_buffer and bound the read_*_no_wait drain loops
Verdict
The unprivileged REACHABILITY of the atkbdc poll layer reproduced live: uid 1999 opened world-writable /dev/psm0, MOUSE_GETHWINFO returned the real PS/2 mouse (buttons=5 iftype=3 hwid=4), and 20x MOUSE_SETMODE drove psm -> send_aux_command -> wait_while_controller_busy/wait_for_aux_ack (worst 7.3ms on the healthy QEMU i8042) - see run.log. The DEFECT itself (unbounded loops when the controller floods data: empty_kbd_buffer :865-882, empty_aux_buffer :905-922, read_kbd_data_no_wait :799-804, read_aux_data_no_wait :838-843 - none carries the 'waited' cap that empty_both_buffers :959-966 has for exactly this scenario) cannot be exercised from guest userspace: injecting an i8042 data flood needs hardware/host-level access (/dev/io is root-only). Hardware-preconditioned local DoS (Low).
No comments yet.