dpt_detect_cache log-page walk bounded by device-fed 16-bit page length (up to 65539) instead of the 512 bytes actually DMA'd β OOB kernel read up to ~64KB past the transfer buffer
Summary
The HBA is asked for exactly 512 bytes but the walk bound is the controller-written header length, and the NEXT macro reads param[3] before the bound check. A hostile/faulty controller reporting a large length walks up to buff+65539, reading kernel memory the device never wrote; values only feed cache_type (no disclosure) - ceiling is a page-fault panic at attach. Arithmetic proof with verbatim macros on the guest (16383 accepted steps; 15361 reads past the whole 4096-byte sg page). Clamp-to-512 fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4244 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.3 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| dpt_df4244_walk.c | β | 4.5 KB | view raw | |
| build.sh | β | 104 B | view raw | |
| run.sh | β | 98 B | view raw | |
| build.log | β | 92 B | view raw | |
| run.log | β | 3.3 KB | view raw | |
| env.txt | β | 1.0 KB | view raw | |
| fix.diff | β | 597 B | view raw | |
| verdict.json | β | 2.0 KB | view raw | |
| manifest.json | β | 767 B | view raw |
DF-4244 β dpt_detect_cache() log-page walk bounded by device-fed length, not by the 512-byte transfer
The HBA is asked for exactly 512 bytes of log page (cp_datalen = htonl(512),
dpt_scsi.c:618), DMA'd into buff = &dccb[1]. The parameter walk's bound,
however, is bytes = DPT_HCP_LENGTH(buff) (dpt_scsi.c:648) β the page
header's 16-bit length field written by the controller (dpt.h:341),
i.e. up to 65539 β and each DPT_HCP_NEXT(param) step reads param[3]
(another device-fed byte) before the bound check at dpt_scsi.c:663-669.
A hostile/faulty controller that reports a large page length walks param
up to ~64 KB past the 512-byte transfer, reading kernel memory the device
never wrote (stale sg-page contents, then adjacent kernel memory past the
page). The values only influence cache_type (no direct disclosure), but a
walk into an unmapped page faults at attach β kernel panic (DoS ceiling).
Reproduce (arithmetic proof, no hardware needed)
- build:
cc -O2 -Wall -o dpt_df4244_walk dpt_df4244_walk.c - run:
./dpt_df4244_walk - expected (guest transcript in run.log): bytes=65539, DMA=512, page=4096 16383 accepted steps, farthest offset buff+65536 16257 iterations read outside the 512-byte DMA buffer 15361 of those read past the entire 4096-byte sg page
DF-4244 verdict β UNTESTED at kernel level (hardware-gated); walk arithmetic PROVEN on guest
Reproduced?
Harness: YES β dpt_df4244_walk.c embeds the DPT_HCP_* macros verbatim (dpt.h:341-344) and the exact loop (dpt_scsi.c:663-669) over a faithful model (512-byte DMA buffer inside a 4096-byte bus_dmamem page). Guest run (run.log):
walk stopped after 16383 accepted steps at buff+65536 reads OUTSIDE the 512-byte DMA buffer : 16257 iterations of which past the whole 4096B sg page: 15361 bound used by driver : &buff[bytes] = buff+65539 <- device-fed bound that is safe : &buff[512] <- actual transfer
Kernel-level: UNTESTED β needs a DPT controller returning a bogus page length; none in the guest (env.txt).
Impact
OOB kernel read (up to ~65 KB past the transfer), values never copied to
userspace (only cache_type decisions, dpt_scsi.c:671-681) β no leak; the
realistic ceiling is a page-fault panic during attach from reading unmapped
kernel VA, plus unbounded loop time. Severity Low.
Fix validation
Not possible without hardware; fix.diff clamps bytes to the 512 bytes
actually requested.
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_testablefix.diff authored against the read-only sys/ tree; kernel-level fix validation impossible without DPT SmartRAID hardware (none in guest, no QEMU emulation exists).
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (DF-4244 block); dpt_scsi.c:618,648,663-669; dpt.h:341-344
PoC changes
No seed PoC existed; evidence authored from scratch in this run.
Verified recommended fix
Clamp bytes to the 512 bytes actually DMA'd before the walk (fix.diff).
Verdict
Untested at kernel level (no DPT hardware; no QEMU emulation). Walk arithmetic proven on the guest with the verbatim macros: with a device-fed length of 65539 the loop accepts pointers to buff+65536 while only 512 bytes were DMA'd β 15361 iterations read past the entire sg page. Bound should be the transfer size, not the device-fed header length.
No comments yet.