Kernel heap info leak in mpr_user_pass_thru: copyout uses user-controlled ReplySize with no upper bound
Summary
mpr_user_pass_thru at mpr_user.c:867/1093: copyout(cm->cm_reply,PTRIN(data->PtrReply),data->ReplySize). ReplySize is uint32_t user-controlled. sz=rpl->MsgLength*4 (actual reply ~96 bytes) but if(sz>ReplySize) only prints warning, does NOT clamp. ReplySize=1MB -> copyout reads 1MB from cm->cm_reply (inside reply_frames DMA pool) -> leaks kernel heap (pointers, DMA addresses, command metadata). Operator group. KASLR bypass. Fix: clamp copyout to min(sz,ReplySize).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1327 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc.c | trigger-source | MPTIOCTL_PASS_THRU with ReplySize=1MiB; dumps leaked reply-pool bytes | 3.5 KB | view raw |
| VERDICT.md | verdict | full source trace: copyout uses user-controlled ReplySize, no clamp | 5.5 KB | β raw |
| fix.diff | suggested-fix | clamp both copyout sites to real reply length sz | 1.2 KB | view raw |
| build.sh | build-script | cc -o poc poc.c -Wall | 381 B | view raw |
| run.sh | run-script | ./poc | 424 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 | 1.2 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-1327 β mpr_user_pass_thru kernel-heap info leak
Status: INCONCLUSIVE (real bug; needs LSI SAS3+ HBA absent from audit guest)
Impact: info leak (kernel heap, 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
(no SAS HBA β driver never attaches β no device node). See VERDICT.md
for the full source trace and fix.diff for the validated patch.
Files
poc.cβ PoC source (opens/dev/mpr0, MPTIOCTL_PASS_THRU, ReplySize=1MiB)VERDICT.mdβ full mechanism trace with path:line citationsfix.diffβ clamp both copyout sites to the real reply lengthszbuild.sh/run.shβ exact repro commandsbuild.log/run.logβ guest build + run outputfix_build.logβ full nativekernel build with the fix applied (rc=0)env.txtβ guest environment + driver-availability factsmanifest.jsonβ machine-readable artifact catalog
DF-1327 β mpr_user_pass_thru kernel-heap info leak
Verdict
INCONCLUSIVE (real bug, needs hardware absent from guest). The
vulnerability is confirmed real by a complete source trace; it cannot
be executed on this QEMU audit guest because the mpr driver has no
LSI SAS3+ HBA to attach to, so /dev/mpr0 does not exist and the PoC
fails at open() with ENOENT. The fix was validated to apply and
compile in a full nativekernel build.
Mechanism (trigger β primitive β effect)
mpr_user_pass_thru() issues a passthrough command to the IOC firmware
and then copies the reply back to userspace. The reply copy is:
sys/dev/raid/mpr/mpr_user.c:857 if ((cm != NULL) && (cm->cm_reply != NULL)) { sys/dev/raid/mpr/mpr_user.c:858 rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; sys/dev/raid/mpr/mpr_user.c:859 sz = rpl->MsgLength * 4; // real reply size (~96 B) sys/dev/raid/mpr/mpr_user.c:861 if (sz > data->ReplySize) { // WARN only, no clamp sys/dev/raid/mpr/mpr_user.c:862 mpr_printf(... "user reply buffer (%d) " sys/dev/raid/mpr/mpr_user.c:864 "smaller than returned buffer (%d)\n", ...); sys/dev/raid/mpr/mpr_user.c:865 } sys/dev/raid/mpr/mpr_user.c:866 mpr_unlock(sc); sys/dev/raid/mpr/mpr_user.c:867 copyout(cm->cm_reply, PTRIN(data->PtrReply), sys/dev/raid/mpr/mpr_user.c:868 data->ReplySize); // *** LEAK: user-controlled len *** sys/dev/raid/mpr/mpr_user.c:869 mpr_lock(sc);
A second, identical site exists in the main pass-thru return path:
sys/dev/raid/mpr/mpr_user.c:1085 sz = rpl->MsgLength * 4; sys/dev/raid/mpr/mpr_user.c:1087 if (sz > data->ReplySize) { mpr_printf(...); } sys/dev/raid/mpr/mpr_user.c:1093 copyout(cm->cm_reply, PTRIN(data->PtrReply), data->ReplySize);
data->ReplySize is a fully user-controlled uint32_t in the
mpr_pass_thru_t ioctl argument (sys/dev/raid/mpr/mpr_ioctl.h:194).
The check at 861/1087 only warns when the user buffer is smaller than
the reply β it never clamps the copyout length. Setting ReplySize
much larger than the actual reply (sz, ~96 bytes) makes copyout()
read ReplySize bytes from cm->cm_reply, which sits inside the
reply_frames DMA pool. Every byte past the real reply is kernel
heap: function pointers, DMA bus addresses, command metadata, and the
contents of adjacent reply frames. This is a deterministic, repeatable
kernel-heap information leak β KASLR-bypass / heap-layout disclosure.
Reachability on this guest
mpris compiled intoX86_64_GENERIC(kldstat -vβpci/mpr).- But there is no LSI/Avogo SAS3+ HBA on the QEMU guest (only
Intel PIIX3 IDE + Virtio block).
mpr_attach_user()therefore never runs, somake_dev()is never called and no/dev/mpr0exists:$ ls /dev/mpr* -> No such file or directory - The PoC (
poc.c) opens/dev/mpr0and issuesMPTIOCTL_PASS_THRU. Run output:poc: open /dev/mpr0: No such file or directory (RUN_EXIT=1) - Privilege model (for a hardware-equipped host): the device node is
created
UID_ROOT, GID_OPERATOR, 0640(mpr_user.c:205);mpr_open()(mpr_user.c:225) returns 0 unconditionally;mpr_ioctl()(mpr_user.c:2259) does nopriv_check()/suser(). So the bug is reachable by root or anyoperator-group member β not a fully-unprivileged user β on a host that has an mpr HBA. The finding's "operator group" framing is correct.
This is the Phase-4(d) case: genuinely not reachable on this kernel guest because the required hardware is absent. It is not a false-positive, not already-fixed, and not a missing-setup we can supply (the setup is physical hardware + operator group, both absent).
Exploit chain
None developed β the primitive is an information leak (read-only OOB),
not a memory-corruption write, so there is no uid=0 chain to pursue
per Phase 6. The realistic impact ceiling is a deterministic
kernel-heap disclosure: leak N bytes of the reply DMA pool per call,
including kernel text/data pointers and DMA bus addresses β defeats KASLR
and reveals heap layout for a follow-on corruption exploit. N is
bounded only by the user's ReplySize (up to ~4 GiB before copyout
faults on an unmapped user page).
PoC changes
The PoC folder was empty (no seeded scaffolding). I authored:
- poc.c β opens /dev/mpr0, issues MPTIOCTL_PASS_THRU with
ReplySize = 1 MiB, hexdumps the leaked reply-pool bytes. The on-wire
mpr_pass_thru_t and MPTIOCTL_PASS_THRU are reproduced inside the PoC
because <dev/raid/mpr/mpr_ioctl.h> is not installed under
/usr/include.
- build.sh / run.sh β exact build/run commands.
Fix
fix.diff clamps both copyout sites to the actual reply length sz
(after clamping sz down to ReplySize when the user buffer is smaller),
so the copyout never reads past cm->cm_reply. The diff applies cleanly
(git apply --check) and was built in a full
make -j6 nativekernel KERNCONF=X86_64_GENERIC on the audit guest
(mpr_user.o recompiled, rc=0, no errors) β see fix_build.log.
Fix validation
fix_status: not_testable. The PoC cannot run on this guest (no
/dev/mpr0), so a runtime before/after comparison is impossible. The
fix was validated to (a) apply to the audit source, (b) compile in a full
nativekernel build, and (c) close the code path by inspection β the
copyout length is now sz (real reply size), not the user-controlled
ReplySize.
Fix verification
not_testablecompile validated
nativekernel rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. mpr_user_pass_thru copyout uses user ReplySize not real sz -> heap info leak. mpr in GENERIC, no SAS HBA.
No comments yet.