⬢ DragonFlyBSD Kernel Audit
← triage · dashboard
DF-4021

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4021 · 2 files
FileTypeDescriptionSize
README.md file 2.3 KB ↓ raw
verdict.json file 1.3 KB view raw
README.md file
↓ download 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 == 0 and then guards the target index with if (target > sc->max_devices) (sys/dev/raid/mpr/mpr_user.c:2240-2244) — target == sc->max_devices passes.
  • 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_devices entries: sc->mapping_table = kmalloc(sizeof(struct dev_mapping_table) * sc->max_devices, ...) (sys/dev/raid/mpr/mpr_mapping.c:2140-2141), so valid indices are 0 .. max_devices-1. Index max_devices reads one element past the end.
  • struct dev_mapping_table (sys/dev/raid/mpr/mprvar.h:113-121, 24-byte stride): dev_handle is the u16 at byte offset 18 of the element, so the OOB load lands at mapping_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 via MPTIOCTL_BTDH_MAPPING (mpr_user.c:2406-2412) on the 0640 root:operator /dev/mprN node (mpr_user.c:205); TargetID is a user-controlled u16 (mpr_ioctl.h:347-353) so TargetID = 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_testable
baseline no→ patch + rebuild →patched clean

change 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
per-fix-DF-4021

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)