mpr_user_pass_thru leaks the user-sized cm->cm_data buffer and its dmamap mapping across the timeout/reinit path
Summary
On pass-thru timeout with reinit-realloc, mpr_wait_command sets *cmp=NULL; the error branch then skips kfree(cm->cm_data) while mpr_free_command only NULLs cm_data - the kmalloc buffer and its loaded dmamap leak per timed-out pass-thru (contrast mpr_user_command which frees unconditionally). Slow unbounded kmem + dmamap exhaustion DoS. Reclaim fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4010 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | readme | path:line trace + fix sketch | 2.7 KB | β raw |
| verdict.json | verdict | machine verdict | 2.2 KB | view raw |
DF-4010 β mpr_user_pass_thru leaks cm->cm_data (and its dmamap mapping) across the reinit path
What
When a pass-thru command times out, mpr_wait_command() (sys/dev/raid/mpr/
mpr.c:3604-3618) performs an IOC reinit; if the reinit reallocated the queues
it sets *cmp = NULL to tell the caller the command was freed internally.
mpr_user_pass_thru() then takes the error branch:
err = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); /* mpr_user.c:1050 */
if (err || (cm == NULL)) {
...
goto RetFree; /* mpr_user.c:1052-1056 */
}
...
RetFree:
if (cm != NULL) {
if (cm->cm_data)
kfree(cm->cm_data, M_MPRUSER); /* mpr_user.c:1149-1151 */
mpr_free_command(sc, cm);
}
With cm == NULL, the kfree(cm->cm_data) is skipped β but the reinit path
that freed the command does not free cm->cm_data either:
mpr_free_command() (mprvar.h:568-611) merely sets cm->cm_data = NULL
without freeing. The user-controlled buffer (kmalloc(MAX(DataSize,
DataOutSize)), mpr_user.c:904-909 β DataSize/DataOutSize are raw user u32s)
is therefore leaked, together with any bus_dmamap mapping loaded onto
it (mpr_map_command β bus_dmamap_load during execution; the unload at
mpr_user.c:1067 only runs on the success path).
Impact
Kernel-memory + dmamap resource leak from the operator-group ioctl surface.
Each cycle costs a 30 s timeout plus a full IOC reset, so exhaustion is slow
but unbounded; with large DataSize values (e.g. 64 MB) dozens of iterations
materially consume kmem and dmamap segments, degrading the driver for all
users (resource-exhaustion DoS). No memory-corruption shape found on this
path (contrast mpr_user_command(), which frees its buf unconditionally
at mpr_user.c:734-735 β the pass-thru path is the one that leaks).
Verification posture
Source-level certain; live trigger needs an mpr controller whose commands time out (30 s) followed by a reallocation-triggering reinit β no LSI SAS hardware on the audit guest, so not live-reproduced (pack documents the path:line trace; no replication needed for a Low resource-leak finding).
Fix sketch
--- a/sys/dev/raid/mpr/mpr_user.c
+++ b/sys/dev/raid/mpr/mpr_user.c
@@
if (err || (cm == NULL)) {
mpr_printf(sc, "%s: invalid request: error %d\n", __func__,
err);
+ if ((cm == NULL) && (data != NULL)) { /* command freed by reinit */
+ /* cm_data was kmalloc'd here and is not freed by
+ * mpr_free_command(); free it to avoid leaking it
+ * (and its dmamap mapping) across the reset. */
+ ...
+ }
goto RetFree;
}
(The clean fix requires mpr_wait_command/mpr_realloc to hand back the
orphaned cm_data pointer, or pass-thru to keep its own reference for the
error path.)
Fix verification
not_testablenot attempted (Low; HW-gated)
Confirmed kernel references
Detail
Evidence (decisive lines)
path trace in README.md: mpr_user.c:1050-1056 (error branch skips free), mpr_user.c:1149-1151 (free gated on cm!=NULL), mprvar.h:568-611 (mpr_free_command NULLs cm_data without kfree), mpr.c:3604-3618 (*cmp=NULL protocol), contrast mpr_user.c:734-735 (mpr_user_command frees buf unconditionally β no leak there)
PoC changes
n/a
Verified recommended fix
Free (or reclaim and unload) the orphaned cm_data on the cm==NULL error path β e.g. have mpr_realloc hand back orphaned cm_data pointers, or keep a local reference in mpr_user_pass_thru and kfree/bus_dmamap_unload it on all error paths.
Verdict
Source-certain resource leak, HW-gated live: on pass-thru timeout with reinit-realloc, mpr_wait_command sets *cmp=NULL (mpr.c:3611-3617) and mpr_user_pass_thru's error branch skips kfree(cm->cm_data) (mpr_user.c:1052-1056 vs 1149-1151) while mpr_free_command never frees cm_data (mprvar.h:568 NULLs it) β the user-sized M_MPRUSER buffer and its loaded dmamap mapping leak per timed-out pass-thru. Low severity (slow kmem/dmamap exhaustion from the operator-group surface, 30s+IOC-reset per iteration). Not live-reproduced: no mpr controller on the guest.
No comments yet.