DF-2284 / verdict.json
{ "finding_id": "DF-2284", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED on the stock INVARIANTS kernel: 400 write-only sg requests (64 KiB each) left M_DEVBUF at 1014 allocations / 25.3 MB against a 211/208K baseline, and the count and bytes stayed identical across last-close + 3 s + 5 s -- sgoninvalidate (scsi_sg.c:169-191, whose own comment says XXX Return all queued I/O with ENXIO) and sgcleanup (:194-206) drain nothing; every sg_rdwr + union ccb + buffer queued by sgwrite (:652/:664/:700/:740) is orphaned forever because only a pack_id-matching sgread (:807,:855-856) ever frees them. A second independent run on the same boot re-leaked exactly ~2000 more allocations, confirming linearity. The invalidation-UAF surface clause of the finding is corroborated by DF-2283 run.log: a reader parked in sgread while the open file description closed wedged the guest (kernel livelock) -- the exact parked-reader-vs-freed-softc interaction the finding describes. RESTRICTEDROOT/operator gated (0600 root-only node on this guest).", "exploit_chain": "", "evidence": "run1.log/phase2.log/phase3.log: M_DEVBUF 211/208K -> 1011/25.3M after 400 write-only requests, unchanged after kill-holder (last close) + 8 s; the second stock run (directclose) re-leaked +800 allocations", "kernel_refs": [ "sys/bus/cam/scsi/scsi_sg.c:652", "sys/bus/cam/scsi/scsi_sg.c:664", "sys/bus/cam/scsi/scsi_sg.c:700", "sys/bus/cam/scsi/scsi_sg.c:740", "sys/bus/cam/scsi/scsi_sg.c:807", "sys/bus/cam/scsi/scsi_sg.c:855", "sys/bus/cam/scsi/scsi_sg.c:169", "sys/bus/cam/scsi/scsi_sg.c:183", "sys/bus/cam/scsi/scsi_sg.c:194", "sys/bus/cam/scsi/scsi_sg.c:205" ], "poc_changes": "v1 measured with a misparsed vmstat column and closed the fd mid-session (colliding with the DF-2282 close-underflow and wedging the guest); v2 forks a holder that parks with the fd open and measures from an external session (phase2/phase3), plus a directclose variant for the close-time question.", "attempts": 5, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 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": 240, "guest_dirty": 1, "build_cmd": "cc -O -Wall -o sg2284 sg2284.c", "run_cmd": "./sg2284 (then external vmstat -m | grep devbuf; kill -9 holder)", "code_hash": "689a76d555dc387bd115a6e62e9a02048b2d763ba1481aff4ea457a5433bd895", "notes": "Fix validation honest result: the drain in fix.diff targets sgoninvalidate/sgcleanup, but on this system the periph is NOT invalidated at last close (CAM keeps the periph with the LUN; /dev/sg0 survives close and camcontrol rescan), so the close-time leak persists on the fix kernel too and the invalidation path itself is not triggerable on a virtio DVD without hot-removal. fix.diff remains correct for the invalidation path the finding names; recorded fix_status=not_testable with this analysis.", "recommended_fix": "Drain rdwr_done in sgoninvalidate (completed entries) and sgcleanup (remainder) before freeing the softc (fix.diff); note a complete fix also wants a close-time path (periph refcounting is DF-2282).", "fix_status": "not_testable", "fix_validation": "fix kernel: close-time retention unchanged (1014/25.3M persisted; directclose re-leaked +756) because last close does not run sgoninvalidate on this system; invalidation requires LUN removal which the QEMU virtio DVD cannot do; fix.diff compiles in the combined fix kernel (nativekernel RC=0)" } |