β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3817

mpr_log_evt_handler(): hexdump() of firmware event data with unvalidated device-controlled length β€” OOB heap read to console

Summary

hexdump(event->EventData, event->EventDataLength, NULL, 0) passes the raw device-supplied U16 (which per MPI2 counts 32-bit words) as a byte count against a ~96-128-byte reply-frame allocation; an oversized device value dumps up to 64 KiB of adjacent kernel heap to the console. Requires root-enabled MPR_EVENT debug plus a broken/malicious firmware event: kernel heap info leak to console/syslog. Convert-and-clamp fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3817 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.6 KB ↓ raw
VERDICT.md β€” 1.6 KB ↓ raw
probe.log β€” 479 B view raw
fix.diff β€” 707 B view raw
manifest.json β€” 766 B view raw
verdict.json β€” 1.5 KB view raw

DF-3817 β€” mpr_log_evt_handler(): hexdump() of EventData with device-controlled length (OOB read of reply-frame buffer to console)

What

mpr_log_evt_handler() (sys/dev/raid/mpr/mpr.c:2053-2078):

case MPI2_EVENT_LOG_DATA:
        mpr_dprint(sc, MPR_EVENT, "MPI2_EVENT_LOG_DATA:\n");
        if (sc->mpr_debug & MPR_EVENT)
                hexdump(event->EventData, event->EventDataLength, NULL, 0);  /* mpr.c:2065 */

event->EventDataLength is a U16 taken verbatim from the firmware's event notification reply (a device-controlled DMA buffer). Per the MPI2 specification it counts 32-bit words, and the driver never validates it against the reply frame size (sc->replyframesz, mpr.c:1346 = facts->ReplyFrameSize * 4, typically 96-128 bytes). hexdump() interprets it as a byte count:

  • semantics bug: under-dumps legitimate events (1/4 of the data);
  • security bug: a firmware value > the remaining frame bytes makes hexdump read up to 65535 bytes past the EventData pointer β€” an OOB read of the kernel heap adjacent to the reply-frames DMA allocation, printed to the console (dmesg/console log).

Gate: requires hw.mpr.N.debug_level to have MPR_EVENT set (root sysctl or loader tunable; the default mpr.c:1672 is MPR_INFO|MPR_FAULT) and a device that sends a LOG_DATA event with an oversized length β€” broken or malicious firmware/DMA. Same device-trust family as DF-3815.

Not runnable here

No SAS3 controller on the guest (probe.log); event delivery cannot occur.

Fix

fix.diff: convert words→bytes and clamp to the bytes remaining in the reply frame.

VERDICT.md
↓ download raw

DF-3817 β€” VERDICT

status: inconclusive (runtime) β€” no controller on the guest to deliver events; source defect certain, debug-path-gated.

Trace

  • Handler registration: mpr_attach_log() mpr.c:2081-2093 sets MPI2_EVENT_LOG_DATA in the mask; events are delivered via mpr_dispatch_event() (mpr.c:2451-2476) from the interrupt path with reply pointing into the sc->reply_frames DMA allocation (mpr.c:2356-2358, range-checked only against the frame-pool bounds at mpr.c:2365-2377 β€” not against the per-message declared length).
  • The dump: mpr.c:2064-2066 β€” hexdump(event->EventData, event->EventDataLength, NULL, 0). EventDataLength is the raw U16 from the reply (no le16toh, no clamp); MPI2 defines it in dwords.
  • Best case: prints 1/4 of the event. Worst case (device value larger than the frame): reads up to 64 KiB past a ~96-128-byte DMA-coherent buffer β†’ kernel heap contents to console/syslog.

Impact ceiling

Privileged debug flag + device cooperation β†’ console info leak of kernel heap adjacent to the reply pool; panic unlikely (hexdump reads only). Low severity, hardening class.

Fix

fix.diff: hexdump(event->EventData, imin(le16toh(event->EventDataLength) * 4, sc->replyframesz - offsetof(MPI2_EVENT_NOTIFICATION_REPLY, EventData)), NULL, 0).

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_testable
baseline no→ patch + rebuild →patched clean

No controller on guest; event path cannot be exercised.

fix.diff; probe.log.
↓ fix.diffper-fix-DF-3817

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md; fix.diff (words->bytes + clamp to replyframesz).

PoC changes

No runnable PoC without hardware.

Verified recommended fix

Convert EventDataLength words to bytes and clamp to the remaining reply-frame bytes before hexdump().

Verdict

Source-certain: mpr.c:2065 passes the raw device-supplied U16 EventDataLength (dwords per MPI2, never validated) to hexdump() as a byte count against a ~96-128-byte reply-frame allocation β€” oversized values dump up to 64 KiB of adjacent kernel heap to the console. Gated behind MPR_EVENT debug (root) plus device cooperation. Runtime inconclusive: no SAS3 controller on the guest, events cannot be delivered (probe.log).