cam_devq reference count is a non-atomic int and cam_devq_release() frees the devq with active I/O after only a warning
Summary
Plain ++/-- on devq->refcount with no lock/atomics; the final release merely kprintf's when alloc_active/send_active are non-zero then frees anyway: two SIMs sharing one devq concurrently registered/released (kldload/kldunload churn) can lose an update -> premature free -> UAF of the queue arrays under the scheduler. No in-tree guest-reachable concurrent shared-devq (de)registration (virtio/nata use private devqs) - hardening class. Atomic-cmpset fix mirroring the file's xpt_release_* neighbors (cam_xpt.c:4979-5230).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5552 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| verdict.json | β | 1.7 KB | view raw | |
| env.txt | β | 239 B | view raw |
DF-5552 β cam_devq reference count is a non-atomic int and cam_devq_release() frees a devq with active I/O after only a warning
File: sys/bus/cam/cam_queue.c:241-257 (cam_devq_reference,
cam_devq_release), consumers sys/bus/cam/cam_sim.c:151,185
(cam_sim_release β cam_simq_release, cam_sim_alloc β
cam_devq_reference).
++devq->refcount/--devq->refcountare plain, non-atomic, and lock-free. Two SIMs sharing one devq (in-tree:cam_sim_alloc(..., queue)with a passed devq β e.g. mpt's cam_sim + phydisk_sim pair) that are concurrently registered/released (kldload/kldunload, driver attach/detach storm, device re-enumeration) can lose an update: the count drops to 0 while a live sim still holds the devq βcamq_fini()+kfree(devq)under the scheduler's feet β use-after-free of the alloc/send queue arrays and the devq itself.cam_devq_release()at refcount 0 merelykprintfs a warning whenalloc_active/send_activeare non-zero (cam_queue.c:251-252) and frees anyway β a detach racing in-flight I/O turns the warning into a UAF instead of a refusal.
No PoC built: triggering requires concurrently (de)registering two SIMs that share one devq β a driver-orchestrated state no in-tree path reaches on this guest (virtio/nata each allocate a private devq). Hardening-class: Low severity, analysis-only.
Fix sketch: make the refcount atomic_add_int/atomic_cmpset_int-based
(the file's neighbors xpt_release_bus/xpt_release_target/xpt_release_device
in cam_xpt.c:4979-5230 already use that pattern), and refuse (or leak with
a warning) the final free while alloc_active/send_active != 0.
Fix verification
not_testablenot_applicable (no PoC; hardening)
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md code analysis with path:line citations
PoC changes
n/a (no PoC built)
Verified recommended fix
atomic refcount (atomic_add_int/cmpset) for cam_devq_reference/cam_devq_release and refuse the final free while alloc_active/send_active != 0
Verdict
cam_devq_reference/cam_devq_release (cam_queue.c:241-257) use a plain non-atomic ++/-- on devq->refcount with no lock, and the final release frees the devq (camq_fini + kfree) after only a kprintf warning when alloc_active/send_active are non-zero. Two SIMs sharing one devq concurrently registered/released can lose a refcount update -> premature free -> UAF of the queue arrays under the scheduler. Analysis-only: no in-tree guest-reachable path registers two sims on one shared devq concurrently (virtio/nata use private devqs); hardening-class, PoC not built per contract for Low findings.
No comments yet.