mrsas_cam_attach error paths free sim_0/sim_1 without clearing sc pointers β attach_fail_cam unwind re-frees via mrsas_cam_detach: cam_sim_path() UAF read, second xpt_bus_deregister, cam_sim_free() double-free into M_CAMSIM (plus devq refcount underflow)
Summary
Four failure paths of mrsas_cam_attach free the cam_sim objects without NULLing sc->sim_0/sim_1; mrsas_attach unwinds via mrsas_cam_detach which re-processes the dangling pointers: UAF read in cam_sim_path, double bus-deregistration, double kfree into M_CAMSIM, devq refcount underflow. Trigger is CAM resource exhaustion at attach on Thunderbolt-MR hardware (absent from guest). Mirror choreography compiled and executed ON the guest proves both violations and that the NULL-clearing fix is clean. NULL-after-free fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4438 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.4 KB | β raw | |
| VERDICT.md | β | 4.6 KB | β raw | |
| repro_logic.c | β | 5.1 KB | view raw | |
| build.sh | β | 118 B | view raw | |
| run.sh | β | 122 B | view raw | |
| build.log | β | 128 B | view raw | |
| run.log | β | 613 B | view raw | |
| env.txt | β | 423 B | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 3.5 KB | view raw |
DF-4438 β mrsas_cam_attach error paths leave dangling sc->sim_0/sc->sim_1; caller unwind double-frees the cam_sim
File: sys/dev/raid/mrsas/mrsas_cam.c
Severity: Low Β· CWE: CWE-415 (Double Free) / CWE-459 (Incomplete Cleanup) Β· Confidence: certain (static call-sequence proof + on-guest logic mirror)
What
mrsas_cam_attach() frees sim_0/sim_1 on its internal failure paths without
clearing sc->sim_0 / sc->sim_1. When it returns a failure, mrsas_attach()
(sys/dev/raid/mrsas/mrsas.c:805 β attach_fail_cam: at :838-839) calls
mrsas_cam_detach(), which unconditionally re-processes the same non-NULL pointers:
/* mrsas_cam.c:216-218 */
if (sc->sim_0) {
xpt_bus_deregister(cam_sim_path(sc->sim_0)); /* UAF read of freed sim;
double-deregister */
cam_sim_free(sc->sim_0); /* kfree() again */
}
In DF CAM, cam_sim_free() β cam_sim_release() (sys/bus/cam/cam_sim.c:231-251)
kfree's the sim when refcount == 1 and drops the sim's devq reference. The
deregister-at-failure paths (mrsas_cam.c:150-156, :157-165, :181-185,
:186-193) already executed exactly that, so the second cam_sim_free() is a
double free into M_CAMSIM, preceded by a use-after-free read in
cam_sim_path() (sim->path_id) and a second xpt_bus_deregister() of a bus
that is already deregistered (or was never registered, on the
xpt_bus_register-failure path) β corrupting CAM bus bookkeeping and the
devq refcount (cam_simq_release() underflow β premature cam_devq free).
Live failure paths (all reachable under kernel memory pressure / CAM resource exhaustion at driver attach β boot or card hotplug):
| mrsas_cam.c line | failure | frees | dangling |
|---|---|---|---|
| 150-156 | xpt_bus_register(sim_0) != CAM_SUCCESS |
devq ref + sim_0 |
sc->sim_0 |
| 157-165 | xpt_create_path(&path_0) fails |
devq ref + sim_0 |
sc->sim_0 |
| 181-185 | xpt_bus_register(sim_1) fails |
sim_1 |
sc->sim_1 |
| 186-193 | xpt_create_path(&path_1) fails |
sim_1 |
sc->sim_1 |
(The sim_1 == NULL path at mrsas_cam.c:175-178 is dead code β cam_sim_alloc()
allocates with M_INTWAIT and cannot return NULL; it also leaks the caller's devq
reference.)
How to reproduce (logic mirror, runs on the DragonFly guest)
The trigger requires an LSI/Avago Thunderbolt-MR controller and CAM resource
failure at attach; the audit QEMU guest has no such device (see env.txt), so the
kernel sequence cannot be executed there. Instead repro_logic.c mirrors the exact
kernel refcount/cleanup choreography (cam_sim_alloc/cam_sim_release semantics
from sys/bus/cam/cam_sim.c:165-251 + the mrsas call sequence) and detects the
UAF/double-free:
./build.sh # cc -O2 -Wall -o repro_logic repro_logic.c ./run.sh # buggy mode: flags UAF + double free (rc=2); fixed mode: clean (rc=0)
Expected decisive output (run.log):
!! sim UAF: cam_sim_path() on freed cam_sim (kernel: read of kfree'd M_CAMSIM) !! sim DOUBLE FREE: cam_sim_release()/kfree() on already-freed cam_sim RESULT: 2 kernel-memory-safety violation(s) detected ...
Fix
fix.diff β NULL out sc->sim_0/sc->sim_1 after each error-path
cam_sim_free() (and defensively in mrsas_cam_detach()), plus release the
caller's devq reference on the (currently dead) sim_1-alloc-failure path.
DF-4438 VERDICT
Status: inconclusive (kernel-path reproduction impossible on this guest: no LSI/Avago MR controller present) Β· Logic-flaw proof: certain Β· Impact if triggered: kernel heap double-free (M_CAMSIM) + UAF read + CAM bus/devq state corruption at driver-attach failure.
Root cause, traced line by line
-
mrsas_attach()callsmrsas_cam_attach(sc)βsys/dev/raid/mrsas/mrsas.c:805. On failure it goes toattach_fail_cam:(mrsas.c:838-839) which callsmrsas_cam_detach(sc)β the same teardown used for a successful attach. -
Inside
mrsas_cam_attach()(sys/dev/raid/mrsas/mrsas_cam.c:116-201), four internal failure paths free the SIM objects themselves and return failure:
:150-156βxpt_bus_register(sc->sim_0, 0)fails βcam_simq_release(devq); cam_sim_free(sc->sim_0);β returnENXIO.:157-165βxpt_create_path(&sc->path_0, ...)fails βxpt_bus_deregister(cam_sim_path(sc->sim_0)); cam_simq_release(devq); cam_sim_free(sc->sim_0);β returnENXIO.:181-185βxpt_bus_register(sc->sim_1, 1)fails βcam_sim_free(sc->sim_1);β returnENXIO.:186-193βxpt_create_path(&sc->path_1, ...)fails βxpt_bus_deregister(cam_sim_path(sc->sim_1)); cam_sim_free(sc->sim_1);β returnENXIO.
None of them clears sc->sim_0 / sc->sim_1.
-
cam_sim_free()in DF is terminal:sys/bus/cam/cam_sim.c:229-231βcam_sim_release(sim, CAM_SIM_SOFTC)at:234-251; withrefcount == 1(:238) it drops the sim's devq reference andkfree(sim, M_CAMSIM)(:243-249). The softc pointer now dangles. -
The caller unwind then runs
mrsas_cam_detach()(mrsas_cam.c:209-227): -:217xpt_bus_deregister(cam_sim_path(sc->sim_0))βcam_sim_path()is a plainsim->path_iddereference of freed memory (UAF read), and the bus is deregistered a second time (or, on path (1), was never registered βxpt_bus_deregisterof an unknown path corrupts/double-unlinks CAM bus bookkeeping). -:218cam_sim_free(sc->sim_0)β double free intoM_CAMSIM, plus a secondcam_simq_release(sim->devq)β devq refcount underflow β prematurecam_devqfree while other sims/CAM still reference it.
Same for sc->sim_1 at :222-224 for failure paths (3)/(4).
Reachability / threat
Requires mrsas(4) attach to fail after cam_sim_alloc β i.e. an LSI Thunderbolt
MR controller present plus kernel memory pressure / CAM resource exhaustion at
boot or card hot-plug (xpt_bus_register/xpt_create_path ENOMEM-class failures).
Not directly attacker-triggerable without those preconditions, hence Low; the
consequence is kernel heap corruption in M_CAMSIM (double free) and CAM-layer
state corruption β panic at minimum, in principle a heap-manipulation primitive.
Evidence
repro_logic.c+build.sh+run.shβ userspace mirror of the exact kernel choreography, compiled and executed on the DragonFly guest (run.log): buggy (current-kernel) sequence flagssim UAF: cam_sim_path() on freed cam_simandsim DOUBLE FREE: cam_sim_release()/kfree() on already-freed cam_sim; fixed sequence (sc->sim_0 = NULLafter error-path free) is clean.cc 8.3 [DragonFly]produced no warnings.env.txtβ guestuname,pciconfshows no vendor-1000 (LSI) devices, no/dev/mrsas*, nohw.mrsassysctl node β kernel-path reproduction is impossible on this guest; the finding rests on the (deterministic, single-threaded, attach-context) call-sequence proof above.
Fix validation
Not kernel-testable on this guest (no mrsas hardware; fix validation requires
building a kernel with the mrsas driver and attaching a device that fails
registration). The mirror's fixed mode demonstrates the fix removes both
violations. fix.diff authored against the read-only tree; never applied to sys/.
Fix summary
Clear sc->sim_0/sc->sim_1 after error-path cam_sim_free() (and NULL them in
mrsas_cam_detach() after freeing) so mrsas_attach's unwind via
mrsas_cam_detach cannot re-free; also drop the caller's devq reference on the
sim_1-alloc-failure path (currently a leak, in dead code since cam_sim_alloc
uses M_INTWAIT).
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_testableKernel fix validation not testable: the guest has no LSI MR controller, and the bug requires attach-time CAM resource failure with the device present. The mirror's fixed mode (sc->sim_X = NULL after free) shows both violations eliminated; fix.diff applies cleanly against sys/dev/raid/mrsas/mrsas_cam.c.
run.log fixed-mode section: 'RESULT: clean (no UAF / double free detected)', exit 0; fix.diff in this pack.
Confirmed kernel references
- sys/dev/raid/mrsas/mrsas_cam.c:150
- sys/dev/raid/mrsas/mrsas_cam.c:153
- sys/dev/raid/mrsas/mrsas_cam.c:162
- sys/dev/raid/mrsas/mrsas_cam.c:182
- sys/dev/raid/mrsas/mrsas_cam.c:190
- sys/dev/raid/mrsas/mrsas_cam.c:216
- sys/dev/raid/mrsas/mrsas_cam.c:217
- sys/dev/raid/mrsas/mrsas_cam.c:218
- sys/dev/raid/mrsas/mrsas.c:805
- sys/dev/raid/mrsas/mrsas.c:839
- sys/bus/cam/cam_sim.c:182
- sys/bus/cam/cam_sim.c:231
- sys/bus/cam/cam_sim.c:243
Detail
Evidence (decisive lines)
run.log: 'sim UAF: cam_sim_path() on freed cam_sim' + 'sim DOUBLE FREE: cam_sim_release()/kfree() on already-freed cam_sim' (buggy, rc=2) vs 'RESULT: clean' (fixed, rc=0); env.txt documents guest device absence; VERDICT.md has the full path:line trace (mrsas.c:805->838-839 -> mrsas_cam.c:216-218; cam_sim.c:231-251 frees at refcount==1).
PoC changes
No seed PoC existed (pass-2 finding discovered in this run); wrote repro_logic.c as a userspace mirror of the kernel refcount/unwind choreography since the trigger needs absent hardware.
Verified recommended fix
NULL sc->sim_0/sc->sim_1 after error-path cam_sim_free() calls in mrsas_cam_attach (and defensively in mrsas_cam_detach) so mrsas_attach's attach_fail_cam unwind cannot double-free; release the caller's devq ref on the sim_1-alloc-failure path.
Verdict
Kernel-path reproduction impossible on the audit guest (no LSI/Avago MR controller: pciconf shows no vendor-1000 devices, no /dev/mrsas*, no hw.mrsas sysctl). The flaw itself is statically certain: mrsas_cam_attach error paths (mrsas_cam.c:150-156, 157-165, 181-185, 186-193) cam_sim_free() sim_0/sim_1 without clearing sc->sim_0/sc->sim_1; mrsas_attach (mrsas.c:838-839) then unwinds via mrsas_cam_detach, which re-processes the dangling pointers: cam_sim_path() UAF read + second xpt_bus_deregister + cam_sim_free() double free into M_CAMSIM plus devq refcount underflow. A userspace mirror of the exact choreography, compiled and run on the DragonFly guest (cc 8.3), flags both violations in buggy mode and is clean with the one-line fix (run.log).
No comments yet.