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

Kernel heap info leak in mpr_user_event_report: copyout of user-supplied Size from fixed-size recorded_events[40000]

Summary

mpr_user_event_report at mpr_user.c:2075-2079: if(size>=sizeof(sc->recorded_events)) copyout(recorded_events,...,size). size=data->Size (uint32_t user-controlled, no upper bound). sizeof(recorded_events)=40000 bytes. Size=0xFFFFFFFF -> copyout reads 4GB from recorded_events inside mpr_softc -> leaks entire softc tail (DMA bus addresses, kernel pointers) + adjacent heap. Operator group. Fix: always copyout sizeof(recorded_events).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1328 Β· 12 files
FileTypeDescriptionSize
poc.c trigger-source MPTIOCTL_EVENT_REPORT with Size=256KiB (>= 40000); dumps softc tail 2.4 KB view raw
VERDICT.md verdict full source trace: copyout length is user Size, gate is lower-bound only 3.2 KB ↓ raw
fix.diff suggested-fix copyout exactly sizeof(recorded_events), independent of user Size 666 B view raw
build.sh build-script cc -o poc poc.c -Wall 183 B view raw
run.sh run-script ./poc 372 B view raw
build.log build-log guest build output (BUILD_EXIT=0) 13 B view raw
run.log run-log guest run: open /dev/mpr0 ENOENT (no HBA) 81 B view raw
fix_build.log build-log full nativekernel build with all 3 mpr fixes, rc=0 5.6 MB ↓ download
env.txt environment guest uname, cc, driver-availability facts 1.5 KB view raw
README.md readme summary + repro + file index 923 B ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme summary + repro + file index
↓ download raw

DF-1328 β€” mpr_user_event_report kernel-heap info leak

Status: INCONCLUSIVE (real bug; needs LSI SAS3+ HBA absent from audit guest) Impact: info leak (kernel heap past recorded_events[40000], operator-group reachable) Driver: mpr (LSI MPT-Fusion 3 SAS) β€” in X86_64_GENERIC but no HBA on guest

Build & run

./build.sh        # cc -o poc poc.c -Wall
./run.sh          # ./poc   (as an operator-group user on an mpr-equipped host)

On the audit guest the PoC fails at open("/dev/mpr0") with ENOENT. See VERDICT.md for the full trace and fix.diff for the validated patch.

Files

  • poc.c β€” PoC (MPTIOCTL_EVENT_REPORT, Size=256KiB β‰₯ 40000)
  • VERDICT.md β€” full mechanism trace with path:line citations
  • fix.diff β€” copyout exactly sizeof(recorded_events), not user size
  • build.sh/run.sh, build.log/run.log, fix_build.log, env.txt, manifest.json
VERDICT.md verdict full source trace: copyout length is user Size, gate is lower-bound only
↓ download raw

DF-1328 β€” mpr_user_event_report kernel-heap info leak

Verdict

INCONCLUSIVE (real bug, needs hardware absent from guest). Source trace confirms the vulnerability; it cannot be executed on this QEMU guest because /dev/mpr0 does not exist (no LSI SAS3+ HBA), so the PoC fails at open() with ENOENT. Fix validated to apply + compile in a full nativekernel build.

Mechanism

sys/dev/raid/mpr/mpr_user.c:2074  mpr_lock(sc);
sys/dev/raid/mpr/mpr_user.c:2075  size = data->Size;                         // uint32_t, user-controlled
sys/dev/raid/mpr/mpr_user.c:2076  if ((size >= sizeof(sc->recorded_events)) && (status == 0)) {
sys/dev/raid/mpr/mpr_user.c:2077      mpr_unlock(sc);
sys/dev/raid/mpr/mpr_user.c:2078      if (copyout((void *)sc->recorded_events,
sys/dev/raid/mpr/mpr_user.c:2079          PTRIN(data->PtrEvents), size) != 0)   // *** LEAK: len = user size ***
sys/dev/raid/mpr/mpr_user.c:2080          status = EFAULT;
sys/dev/raid/mpr/mpr_user.c:2081      mpr_lock(sc);

sizeof(sc->recorded_events) = MPR_EVENT_QUEUE_SIZE (200) Γ— sizeof(mpr_event_entry_t) (= 4+4+4*MPR_MAX_EVENT_DATA_LENGTH = 4+4+4*48 = 200) = 40000 bytes (sys/dev/raid/mpr/mpr_ioctl.h:206, :207, :224-229; sys/dev/raid/mpr/mprvar.h:445).

The gate at 2076 is a lower bound, not an upper bound: it only requires size >= 40000. The copyout length is the user-supplied size, which can be up to 0xFFFFFFFF (4 GiB). Setting size to, say, 256 KiB reads 256 KiB out of sc->recorded_events β€” 40000 bytes of the array plus ~220 KiB of whatever follows it inside struct mpr_softc (DMA bus addresses, kernel pointers, locks, command rings) and into adjacent kernel heap. Deterministic kernel-heap information leak.

Reachability on this guest

Identical to DF-1327: mpr is compiled into GENERIC but no SAS HBA is present β†’ no /dev/mpr0 β†’ PoC fails at open():

poc: open /dev/mpr0: No such file or directory   (RUN_EXIT=1)

Privilege model is the same (device UID_ROOT/GID_OPERATOR 0640, mpr_open returns 0, mpr_ioctl does no priv_check): reachable by root or operator-group users on an mpr-equipped host.

Phase-4(d) again: real code path, unreachable on this guest due to absent hardware.

Exploit chain

None β€” read-only OOB info leak, no corruption primitive. Impact ceiling: deterministic disclosure of the mpr_softc tail (DMA addresses, kernel pointers) and adjacent heap, repeated to taste. KASLR-bypass / heap layout reconnaissance.

PoC changes

Folder was empty. Authored poc.c (opens /dev/mpr0, issues MPTIOCTL_EVENT_REPORT with Size = 256 KiB, dumps bytes 40000..40256 of the softc tail), build.sh, run.sh.

Fix

fix.diff makes the copyout length sizeof(sc->recorded_events) (the fixed array size) instead of the user-controlled size. The size >= gate remains the lower-bound "did the user give us a big enough buffer?" check; the actual bytes copied are now exactly the array. Applies cleanly and was built in the full nativekernel run (fix_build.log).

Fix validation

fix_status: not_testable β€” PoC cannot run (no /dev/mpr0). Fix validated by apply + compile + code-path inspection (copyout length is now the constant array size, independent of data->Size).

Fix verification

not_testable

compile validated

nativekernel rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. mpr_user_event_report copyout uses user Size not sizeof(recorded_events) -> heap info leak. mpr in GENERIC.