DF-5315 / verdict.json
{ "finding_id": "DF-5315", "status": "reproduced", "verdict": "Unprivileged USB_FS_OPEN x USB_FS_OPEN on one ugen fd slot double-enters usbd_transfer_setup() (unlocked EBUSY check at usb_generic.c:1462; setup NULLs its slot at entry, usb_transfer.c:935, sleeps on enum lock + kmalloc, stores the pointer only at :1138). Every barrier-synchronised round double-won: 121 opens -> 61 orphaned usb_xfer root blocks (~1 MB M_USB leaked in 20 s, permanent) and endpoint->refcount_alloc pinned at USB_EP_REF_MAX=0x3F, so after full cleanup (CLOSE+UNINIT+re-INIT) USB_FS_OPEN fails ENOMEM forever -- the control endpoint is dead for ugen ioctls, read()/write() and kernel drivers until replug/reboot. Distinct from DF-5287 (read()/write() path on f->xfer[]): this is the ioctl path on f->fs_xfer[], and the DF-5287/5288 fixes do not cover it.", "reproduced": true, "impact": "dos", "confidence": "certain", "exploit_chain": "unpriv maxx -> shared O_RDWR fd on /dev/usb/0.2.0 -> FS_INIT -> two threads barrier-fire USB_FS_OPEN(ep0) -> both pass the unlocked slot check -> both run sleeping setups -> second entry NULLs/overwrites firsts slot pointer -> orphaned xfer keeps endpoint->refcount_alloc and its ~17 KB root block -> repeat 61x -> endpoint permanently rejects all setups (ENOMEM) + unbounded kernel memory leak per endpoint/device replugged. No memory-corruption face: the orphan is never freed nor reused, only leaked.", "evidence": "run.log (baseline: counters freeze 60+61, POST-CLEANUP FAILED ENOMEM, vmstat 16/19.8K -> 331/1.02M), run.patched.log (600 serialised opens, POST-CLEANUP SUCCEEDED, zero orphan growth)", "kernel_refs": [ "sys/bus/u4b/usb_generic.c:1456-1465", "sys/bus/u4b/usb_generic.c:1538-1540", "sys/bus/u4b/usb_transfer.c:935", "sys/bus/u4b/usb_transfer.c:942", "sys/bus/u4b/usb_transfer.c:1118-1121", "sys/bus/u4b/usb_transfer.c:1138", "sys/bus/u4b/usb_transfer.c:1220" ], "poc_changes": "Seed sketched from the DF-5287 rig (same device, barrier-synced double-entry, refcount-exhaustion probe). Fixed two harness bugs found during verification: (1) probe-open left the slot occupied -> next round armed EBUSY (now closes the probe); (2) shutdown barrier had main participating in a racer-only 2-barrier -> deadlock (main now signals stop via flag only).", "attempts": 4, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 400, "guest_dirty": 1, "build_cmd": "cc -O -Wall -o df5315 df5315.c -pthread -include stdlib.h", "run_cmd": "./df5315 20 (unprivileged maxx, /dev/usb/0.2.0 chmod 0666)", "code_hash": "a4e039aa067acf46045ce3e467b6a8b4f4b94669ba137ea5eec1cf077239267d", "notes": "Race won essentially every round (setup window is ~200 ms). An exploratory run on an earlier boot reproduced the identical signature. The orphaned blocks are only visible via vmstat -m (M_USB) -- no other userland observable.", "recommended_fix": "Claim the fs_xfer slot under priv_lock with a sentinel before entering usbd_transfer_setup(), run the setup into a local pointer, publish/clear under the lock (DF-5314/fix.diff USB_FS_OPEN hunk)", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "DF-5314/fix.diff (which carries the USB_FS_OPEN setup-claim) built and booted: opens serialise (600 successes/12 s), no refcount exhaustion, POST-CLEANUP OPEN SUCCEEDED, vmstat -m USB shows zero orphan growth after the full validation suite (baseline: 61 orphans + permanent ENOMEM).", "fix_evidence": "run.patched.log, ../DF-5314/fix_build.log, ../DF-5314/run.patched.log" } |