DF-4454 / run.log
DF-4454 โ run log (assembled from live guest outputs; guest-side originals
under /root/uaf*.log on the audit guest, plus captured transcripts; the
stress run later wedged the guest so files were re-read after recovery)
=== BUILD ===
$ cc -O2 -Wall -o /root/evdev_uaf /root/evdev_uaf.c -lpthread
BUILD-OK
=== RUN 1 (8 MB write, del mid-flight, re-add after 60 ms) ===
[+] stale fd 3 open on /dev/input/event4
[+] writer thread started (8 MB)
[.] host should now: device_del kbd1; sleep 0.06; device_add usb-kbd
[+] new fd 4 open on re-created /dev/input/event4
[MAGIC] type=0 code=0 value=0x1337
[MAGIC] type=0 code=0 value=0x1337
[MAGIC] type=0 code=0 value=0x1337
[MAGIC] type=0 code=0 value=0x1337
[MAGIC] type=0 code=0 value=0x1337
[!] read: Bad file descriptor
[.] waiting for writer to finish...
[.] closing stale fd 3 (evdev_dtor -> EVDEV_LOCK on freed/reused evdev)
UAF-CONFIRMED: total_events=38 magic_hits=19 writer_bytes=0 writer_err=22
RC=42
interpretation: the write() that started BEFORE device_del kept running
through evdev_free() and, after the re-add allocated a new evdev_dev on
the same block, injected 19+ {EV_SYN,SYN_REPORT,0x1337} reports into the
NEW device. The new fd was never written to. write() finally failed with
EINVAL (writer_err=22) once the reallocated evdev's flag bitmap changed
under the stale loop (evdev_check_event now rejecting) โ further proof
the loop was operating on foreign memory.
=== RUN 2 (64 MB write, del mid-flight, re-add after 80 ms) ===
UAF-CONFIRMED: total_events=10 magic_hits=5 writer_bytes=67108848 writer_err=0
RC=42
interpretation: full 64 MB write(2) returned SUCCESS even though the
device was deleted mid-call: ~2.79 million events were injected through
the dangling `evdev' pointer cached by evdev_write() at entry
(sys/dev/misc/evdev/cdev.c:238). 5 magic reports reached the re-created
device's client ring before it overflowed.
=== RUN 3 (64 MB write) ===
UAF-CONFIRMED: total_events=4 magic_hits=2 writer_bytes=67108848 writer_err=0
RC=42
=== RUN 4 โ STRESS (single 256 MB write, 6x device_del/device_add churn) ===
After ~4 churn cycles the guest hard-wedged:
- ssh: "Connection timed out during banner exchange", vm.sh status => down
- serial console (boot.log) silent, ends at "ukbd0 ... (disconnected)"
- qemu-system-x86_64 at 87-394 %CPU (vCPUs spinning in-kernel)
- no panic banner; system unresponsive for several minutes, then the
lock contention cleared by itself and ssh came back.
serial tail saved in wedge_serial_tail.txt (full log: serial-full.log).
interpretation: repeated kfree/lockinit interleavings with the still
running stale writer corrupted lockmgr state (lk_count), leaving the
kernel livelocked on the damaged lock.
=== UNPRIVILEGED-REACHABILITY NOTE ===
/dev/input/event4 is 0640 root:input and evdev_open() enforces
caps_priv_check_self(SYSCAP_RESTRICTEDROOT) (cdev.c:110), so holding the
fd requires root; the detach side (USB unplug) is physical-access.
The bug is therefore a root/physical-triggered kernel UAF whose write
primitive crosses kernel object boundaries (demonstrated above).