Kernel heap memory disclosure via mps_user_event_report copyout with attacker-controlled length
Summary
mps_user_event_report at mps_user.c:1856-1859: size=data->Size (uint32 user, no upper bound). if(size>=sizeof(recorded_events)) copyout(recorded_events,...,size). recorded_events is 10000 bytes. Size=1MB -> reads 1MB past array through softc (function pointers, DMA handles) -> KASLR bypass. Operator group. Twin of DF-1328 (mpr_user.c). Fix: clamp size to sizeof(recorded_events).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1360 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc.c | trigger-source | opens /dev/mps0, issues MPTIOCTL_EVENT_REPORT with Size=256KiB to leak softc tail | 3.1 KB | view raw |
| fix.diff | suggested-fix | copyout sizeof(recorded_events) instead of user-controlled size | 453 B | view raw |
| build.sh | repro-script | cc -O2 -o poc poc.c | 109 B | view raw |
| run.sh | repro-script | ./poc | 56 B | view raw |
| build.log | build-log | poc build, full output | 91 B | view raw |
| run.log | run-log | open /dev/mps0 ENOENT (no SAS HBA on guest) | 194 B | view raw |
| fix_build.log | fix-build-log | clean mps.ko module build with fix applied, rc=0 | 16.1 KB | view raw |
| env.txt | environment | uname + cc version + dev-node check | 520 B | view raw |
| README.md | readme | summary + reproduce | 917 B | β raw |
| VERDICT.md | verdict | full mechanism + reachability + fix | 3.3 KB | β 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 |
DF-1360 β mps_user_event_report kernel-heap info leak (mps)
Summary
mps_user_event_report (sys/dev/raid/mps/mps_user.c:1859) copyouts
size bytes (uint32 user-controlled) out of the 10000-byte recorded_events
array. The guard at :1857 is a lower bound (size >= 10000), so any size
above 10000 over-reads the softc tail and adjacent heap. Twin of DF-1328 (mpr).
No SAS HBA on the audit guest β /dev/mps0 absent.
Reproduce
./build.sh # cc -O2 -o poc poc.c ./run.sh # ./poc
Expected on this guest: poc: open /dev/mps0: No such file or directory
(errno=2) and RUN_EXIT=1 β the mps driver does not attach (no SAS HBA), so
the leak cannot be exercised here. On an mps-equipped host (root/operator) the
same PoC would leak kernel heap.
Fix
fix.diff changes the copyout length to sizeof(sc->recorded_events).
Validated to apply + compile (mps.ko, clean build rc=0).
DF-1360 β VERDICT
Verdict: INCONCLUSIVE (real bug, needs hardware absent from guest). Source
trace confirms the vulnerability; it cannot be executed on this QEMU guest
because /dev/mps0 does not exist (no LSI MPS SAS HBA), so the PoC fails at
open() with ENOENT. Fix validated to apply + compile in a clean mps.ko
module build.
This is the mps driver's twin of DF-1328 (the identical bug in the mpr
driver's mpr_user.c).
Mechanism
sys/dev/raid/mps/mps_user.c:1856 size = data->Size; // uint32_t, user-controlled sys/dev/raid/mps/mps_user.c:1857 if ((size >= sizeof(sc->recorded_events)) && (status == 0)) { sys/dev/raid/mps/mps_user.c:1858 mps_unlock(sc); sys/dev/raid/mps/mps_user.c:1859 if (copyout((void *)sc->recorded_events, sys/dev/raid/mps/mps_user.c:1860 PTRIN(data->PtrEvents), size) != 0) // *** LEAK: len = user size *** sys/dev/raid/mps/mps_user.c:1861 status = EFAULT;
sizeof(sc->recorded_events) = MPS_EVENT_QUEUE_SIZE (50, mps_ioctl.h:207)
Γ sizeof(mps_event_entry_t) (= 4+4+4*MPS_MAX_EVENT_DATA_LENGTH(48) = 200,
mps_ioctl.h:224-230) = 10000 bytes (mpsvar.h:414).
The gate at 1857 is a lower bound (size >= 10000), not an upper bound.
The copyout length is the user-supplied size, which can be up to
0xFFFFFFFF. Setting size = 256 KiB reads 256 KiB out of recorded_events:
10000 bytes of the array plus ~240 KiB of whatever follows it inside
struct mps_softc (DMA bus addresses, kernel pointers, locks, command rings)
and into adjacent kernel heap. Deterministic kernel-heap information leak.
Reachability on this guest
mps is a device in X86_64_GENERIC but no SAS HBA is present β driver never
attaches β no /dev/mps0 β PoC fails at open():
poc: open /dev/mps0: No such file or directory (errno=2) poc: mps driver not attached on this guest (no SAS HBA) -> cannot reach mps_user_event_report. RUN_EXIT=1
Privilege model (same as mpr, make_dev UID_ROOT/GID_OPERATOR 0640,
mps_open returns 0, no priv_check in mps_ioctl): reachable by root or
operator-group users on an mps-equipped host. Phase-4(d): 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 mps_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/mps0, issues
MPTIOCTL_EVENT_REPORT with Size = 256 KiB, would dump bytes 10000..10064 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 "did the user give a big enough buffer?" check; the actual bytes copied are
now exactly the array. Applies cleanly (patch --dry-run hunk @1857) and the
clean mps.ko build succeeds (rc=0 β fix_build.log). Matches the finding's
proposed fix (clamp size to sizeof(recorded_events)).
Fix validation
fix_status: not_testable β PoC cannot run (no /dev/mps0). Fix validated by
apply + clean module compile + code-path inspection (copyout length is now the
constant array size, independent of data->Size).
Fix verification
not_testablecompile+harness validated
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. mps_user_event_report copyout user Size not sizeof(recorded_events) -> heap info leak. mps in GENERIC, no SAS HBA.
No comments yet.