Integer overflow in mpr_diag_read_buffer bounds check allows OOB read of diag DMA buffer
Summary
mpr_diag_read_buffer at mpr_user.c:1798: if(StartingOffset+BytesToRead>pBuffer->size). Both uint32_t. Wrap: StartingOffset=0x100, BytesToRead=0xFFFFFF00 -> sum=0 <= size. :1812 pData=fw_diag_buffer+0x100 -> copyout reads past DMA allocation -> heap info leak. Requires DIAG capability (common on SAS3+). Fix: check StartingOffset<size, BytesToRead<=(size-StartingOffset).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1329 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| poc.c | trigger-source | MPTIOCTL_DIAG_ACTION READ_BUFFER with StartingOffset=0x100, BytesToRead=0xFFFFFF00 (sum wraps to 0) | 3.3 KB | view raw |
| VERDICT.md | verdict | full source trace: uint32 bounds-check sum wraps, OOB copyout | 3.9 KB | β raw |
| fix.diff | suggested-fix | split bounds check: StartingOffset>=size || BytesToRead>size-StartingOffset | 867 B | view raw |
| build.sh | build-script | cc -o poc poc.c -Wall | 198 B | view raw |
| run.sh | run-script | ./poc | 538 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 | 977 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 |
DF-1329 β mpr_diag_read_buffer integer-overflow OOB read
Status: INCONCLUSIVE (real bug; needs LSI SAS3+ HBA absent from audit guest)
Impact: info leak (OOB read of diag DMA buffer via uint32 wrap, 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; a diag buffer must first be REGISTERED)
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_DIAG_ACTION READ_BUFFER, StartingOffset=0x100, BytesToRead=0xFFFFFF00)VERDICT.mdβ full mechanism trace with path:line citationsfix.diffβ split bounds check to avoid the wrapping uint32 sumbuild.sh/run.sh,build.log/run.log,fix_build.log,env.txt,manifest.json
DF-1329 β mpr_diag_read_buffer integer-overflow OOB read
Verdict
INCONCLUSIVE (real bug, needs hardware absent from guest). Source
trace confirms the integer overflow in the bounds check; it cannot be
executed on this QEMU guest because /dev/mpr0 does not exist (no LSI
SAS3+ HBA). Fix validated to apply + compile in a full nativekernel
build.
Mechanism
sys/dev/raid/mpr/mpr_user.c:1798 if (diag_read_buffer->StartingOffset + sys/dev/raid/mpr/mpr_user.c:1799 diag_read_buffer->BytesToRead > sys/dev/raid/mpr/mpr_user.c:1800 pBuffer->size) { sys/dev/raid/mpr/mpr_user.c:1801 *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; sys/dev/raid/mpr/mpr_user.c:1802 return (MPR_DIAG_FAILURE); sys/dev/raid/mpr/mpr_user.c:1803 } ... sys/dev/raid/mpr/mpr_user.c:1812 pData = (uint8_t *)(sc->fw_diag_buffer + sys/dev/raid/mpr/mpr_user.c:1813 diag_read_buffer->StartingOffset); sys/dev/raid/mpr/mpr_user.c:1814 if (copyout(pData, ioctl_buf, diag_read_buffer->BytesToRead) != 0) sys/dev/raid/mpr/mpr_user.c:1815 return (MPR_DIAG_FAILURE);
StartingOffset, BytesToRead, and pBuffer->size are all uint32_t.
The check at 1798 computes StartingOffset + BytesToRead as a 32-bit
sum, which wraps modulo 2Β³Β². Choose
StartingOffset = 0x00000100, BytesToRead = 0xFFFFFF00:
sum = 0x100000000 β‘ 0 (mod 2Β³Β²), which is <= pBuffer->size for
any registered buffer β the bounds check passes. The subsequent
copyout then reads BytesToRead (β 4 GiB) starting at
fw_diag_buffer + 0x100, far past the DMA allocation β kernel-heap
information leak (and/or a copyout fault when it hits an unmapped
page, depending on layout).
Trigger path
ioctl(MPTIOCTL_DIAG_ACTION) sys/dev/raid/mpr/mpr_user.c:2365 -> mpr_user_diag_action() sys/dev/raid/mpr/mpr_user.c:1993 -> mpr_do_diag_action(ACTION=READ_BUFFER) sys/dev/raid/mpr/mpr_user.c:2015 -> mpr_diag_read_buffer() sys/dev/raid/mpr/mpr_user.c:1772 *** overflow here
Precondition: a diag buffer must first be registered
(MPR_FW_DIAG_TYPE_REGISTER via the same ioctl) so
pBuffer->size is set and mpr_get_fw_diag_buffer_number() finds the
caller's UniqueId (mpr_user.c:1787-1791). Registration uses the
same operator-group ioctl, so a single attacker session registers then
over-reads.
Reachability on this guest
Same as DF-1327/1328: mpr is in GENERIC, no SAS HBA β no /dev/mpr0:
poc: open /dev/mpr0: No such file or directory (RUN_EXIT=1)
Privilege model identical (operator group + HBA on a real host).
Phase-4(d): real code path, unreachable on this guest.
Exploit chain
None β read-only OOB read (the overflow defeats the read bounds check, not a write). Impact ceiling: large kernel-heap disclosure from the diag DMA buffer and adjacent memory; KASLR-bypass / heap reconnaissance.
PoC changes
Folder was empty. Authored poc.c (opens /dev/mpr0, issues
MPTIOCTL_DIAG_ACTION READ_BUFFER with the wrap pair
StartingOffset=0x100, BytesToRead=0xFFFFFF00), build.sh, run.sh.
The PoC notes that a real run must first REGISTER a diag buffer to
obtain a valid UniqueId.
Fix
fix.diff restructures the check to avoid the addition entirely β test
each bound independently:
if (StartingOffset >= pBuffer->size ||
BytesToRead > pBuffer->size - StartingOffset)
reject;
StartingOffset >= size rejects an empty/over-large offset outright;
once it passes, size - StartingOffset cannot underflow, and the second
test bounds BytesToRead against the remaining space with no wrapping
sum. 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). Validated
by apply + compile + inspection (the wrapping addition is gone; both
bounds are checked with non-overflowing arithmetic).
Fix verification
not_testablecompile validated
nativekernel rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. mpr_diag_read_buffer uint32 offset+size wrap -> 4GiB OOB read. mpr in GENERIC, no SAS HBA.
No comments yet.