mpr_user_btdh off-by-one: target == max_devices passes the range check and reads mapping_table[max_devices] out of bounds (twin of DF-4016)
Summary
The Bus/Target->DevHandle path guards with 'target > sc->max_devices' but the mapping table has exactly max_devices entries, so TargetID==max_devices reads one 24-byte element past the heap allocation and discloses a garbage u16 dev_handle to the operator-group caller. '>=' fix. PoC skipped per contract (Low, HW-gated); mps-twin evidence at DF-4016.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4021 · 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 2.3 KB | ↓ raw | |
| verdict.json | file | 1.3 KB | view raw |
DF-4021 — mpr_user_btdh off-by-one: target > sc->max_devices lets TargetID == max_devices read mapping_table[max_devices] out of bounds
Twin of DF-4016 (mps), filed for the sys/dev/raid/mpr sibling. Source
analysis only — PoC skipped per contract (Low severity, HW-gated: the ioctl
needs an attached mpr(4) HBA, absent on the audit guest). The mps twin's
source-verification record is findings/poc/DF-4016/.
Source path
mpr_user_btdh():sys/dev/raid/mpr/mpr_user.c:2210-2256.- Bus/Target→DevHandle mode requires
bus == 0and then guards the target index withif (target > sc->max_devices)(sys/dev/raid/mpr/mpr_user.c:2240-2244) —target == sc->max_devicespasses. - The passed index is immediately used:
dev_handle = sc->mapping_table[target].dev_handle;(sys/dev/raid/mpr/mpr_user.c:2245). - The mapping table has exactly
sc->max_devicesentries:sc->mapping_table = kmalloc(sizeof(struct dev_mapping_table) * sc->max_devices, ...)(sys/dev/raid/mpr/mpr_mapping.c:2140-2141), so valid indices are0 .. max_devices-1. Indexmax_devicesreads one element past the end. struct dev_mapping_table(sys/dev/raid/mpr/mprvar.h:113-121, 24-byte stride):dev_handleis the u16 at byte offset 18 of the element, so the OOB load lands atmapping_table + max_devices*24 + 18— a 2-byte out-of-bounds heap read at the allocation boundary.- If the garbage u16 is nonzero it is disclosed to the caller in
data->DevHandle(mpr_user.c:2246-2247). Reachable by any member of group operator viaMPTIOCTL_BTDH_MAPPING(mpr_user.c:2406-2412) on the 0640root:operator/dev/mprNnode (mpr_user.c:205);TargetIDis a user-controlled u16 (mpr_ioctl.h:347-353) soTargetID = max_devices(e.g. 256-ish for typical IOC facts) is directly requestable.
Impact
One 24-byte-stride OOB heap read disclosing a single garbage u16 to an operator-group caller — Low: info disclosure of adjacent heap bytes, no write, no control over the disclosed value beyond the index. Same geometry and severity as the mps twin (DF-4016).
Fix
Change the guard to reject the boundary index:
- if (target > sc->max_devices) {
+ if (target >= sc->max_devices) {
(sys/dev/raid/mpr/mpr_user.c:2240; the mps twin fix is identical at
sys/dev/raid/mps/mps_user.c:2021.)
Fix verification
not_testablechange the check at mpr_user.c:2240 from 'target > sc->max_devices' to 'target >= sc->max_devices' (and the mps twin at mps_user.c:2021)
source-only
Confirmed kernel references
Detail
Exploit chain
[]
Evidence (decisive lines)
source-only; path:line citations in README.md; mps twin evidence at findings/poc/DF-4016/
Verdict
source-verified; HW-gated (needs attached mpr(4) HBA; PoC skipped per contract for Low)
No comments yet.