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

Unbounded scatter-gather loop overflows pmap->psg[17], tmpSg[512] stack, and PRD heap table

Summary

OsSendCommand CAM_SCATTER_VALID at entry.c:2859: for(idx=0;idx<ccb->csio.sglist_cnt;idx++) writes pCmd->pSgTable[idx]=pmap->psg[idx]. psg[MAX_SG_DESCRIPTORS=17]. sglist_cnt is u16 from userland CAMIOCOMMAND, XPT does NOT validate against SIM segment limit. sglist_cnt>17 -> OOB heap write past psg into adjacent BUS_DMAMAP (pAdapter/dma_map ptrs dereferenced later). fDeviceSendCommand:1787 do{*sg1++=*sg2}while(...) into tmpSg[512]. PRD fill :1853-1860. Operator/root via /dev/passN. Fix: check sglist_cnt<=MAX_SG_DESCRIPTORS.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1284 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source object-level proof: replays OsSendCommand CAM_SCATTER_VALID loop with sglist_cnt=64 > 17 4.3 KB view raw
fix.diff suggested-fix reject CCB (CAM_REQ_TOO_BIG) when sglist_cnt > MAX_SG_DESCRIPTORS 844 B view raw
build.sh repro-script cc -O2 -o harness harness.c 101 B view raw
run.sh repro-script ./harness 60 B view raw
build.log build-log harness build, full output 65 B view raw
run.log run-log harness decisive run: 47 guard entries / 752 bytes overflow 363 B view raw
env.txt environment uname + cc version 188 B view raw
README.md readme summary + reproduce 2.6 KB ↓ raw
VERDICT.md verdict full mechanism + reachability + fix + compile validation 4.1 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
README.md readme summary + reproduce
↓ download raw

DF-1284 β€” Unbounded scatter-gather loop overflows pmap->psg[17] in hptmv OsSendCommand

File: sys/dev/raid/hptmv/entry.c:2859 (sink); :2800 (pSgTable = pmap->psg) Class: CWE-787 Out-of-bounds Write (heap) Severity: High

The bug (source-confirmed)

In OsSendCommand (the SIM XPT_SCSI_IO action handler), the CAM_SCATTER_VALID branch (entry.c:2852) copies a CAM CCB's scatter-gather list into the per-command SG table with an unbounded loop (entry.c:2859):

pCmd->pSgTable = pmap->psg;                          /* :2800 */
...
for (idx = 0; idx < ccb->csio.sglist_cnt; idx++) {   /* :2859 */
    pCmd->pSgTable[idx].dSgAddress = ...;            /* :2860 */
    pCmd->pSgTable[idx].wSgSize    = ...;            /* :2861 */
    pCmd->pSgTable[idx].wSgFlag   = ...;             /* :2862 */
}

pSgTable points at pmap->psg, which is SCAT_GATH psg[MAX_SG_DESCRIPTORS] (osbsd.h:143, MAX_SG_DESCRIPTORS = 17, global.h:47). sglist_cnt is u_int16_t (cam_ccb.h:604) β€” range 0..65535 β€” and is not validated against the SIM's segment limit anywhere in this path. (The other path, hptmv_dmamap_callback, does have HPT_ASSERT(nsegs <= MAX_SG_DESCRIPTORS) at :2652; the CAM_SCATTER_VALID path has none.)

A CCB with sglist_cnt > 17 therefore writes past psg[17] into the rest of the BUS_DMAMAP struct (next/pAdapter/dma_map linkage follow it) and beyond into the adjacent slab object β€” an attacker-controlled heap overflow.

Reachability / threat model

The hptmv driver attaches to HighPoint RocketRAID (RR18xx) controllers. None are present in the audit QEMU guest (pciconf -l shows no such device), so the SIM action path is not runtime-reachable here. On real hardware, an operator/root user with access to a /dev/passN (CAM pass-through) node for an hptmv target can issue a CAMIOCOMMAND with CAM_SCATTER_VALID and a hand-crafted sglist_cnt, reaching the loop. See VERDICT.md.

Reproduce (harness)

./build.sh && ./run.sh

Decisive output:

[DF-1284] attacker sglist_cnt=64 (max allowed for psg[]=17)
[DF-1284] overflow past psg[17]: YES (47 guard entries clobbered; 752 bytes of adjacent heap overwritten)

Fix

fix.diff rejects the CCB (CAM_REQ_TOO_BIG) when sglist_cnt > MAX_SG_DESCRIPTORS, mirroring the existing cleanup path (FreeCommand / dmamap_put / xpt_done). Validated: applies cleanly; entry.c compiles under -Werror via the kmod framework. (A twin unbounded loop exists at entry.c:2524 in fOsBuildSgl; the cited attacker-reachable CAM path is fixed here.)

VERDICT.md verdict full mechanism + reachability + fix + compile validation
↓ download raw

DF-1284 β€” VERDICT

Verdict: REPRODUCED (primitive confirmed at object/harness level; runtime path is hardware-bound and not present on the audit guest).

Mechanism (source trace)

OsSendCommand (sys/dev/raid/hptmv/entry.c) is the SIM XPT_SCSI_IO action handler for HighPoint RocketRAID controllers.

  1. SG table pointer β€” entry.c:2800: c pCmd->pSgTable = pmap->psg; psg is SCAT_GATH psg[MAX_SG_DESCRIPTORS] (osbsd.h:143), 17 entries of 16 bytes each (global.h:103).

  2. Attacker-controlled loop bound β€” entry.c:2859: c for (idx = 0; idx < ccb->csio.sglist_cnt; idx++) { pCmd->pSgTable[idx].dSgAddress = (ULONG_PTR)(UCHAR *)sgList[idx].ds_addr; pCmd->pSgTable[idx].wSgSize = sgList[idx].ds_len; pCmd->pSgTable[idx].wSgFlag = (idx==ccb->csio.sglist_cnt-1)?SG_FLAG_EOT:0; } sglist_cnt is u_int16_t (cam_ccb.h:604), supplied by userland in the CAM CCB (e.g. via CAMIOCOMMAND/pass(4)). The CAM XPT layer does not clamp it to the SIM's advertised segment limit, and this branch has no local check.

  3. Contrast with the safe sibling β€” entry.c:2652 (hptmv_dmamap_callback, the non-CAM_SCATTER_VALID path) has HPT_ASSERT(nsegs <= MAX_SG_DESCRIPTORS). The CAM_SCATTER_VALID path has no equivalent guard.

sglist_cnt > 17 overflows psg[], clobbering the trailing BUS_DMAMAP fields (next, pAdapter, dma_map) and the adjacent slab object β€” an attacker-shaped heap overflow (dSgAddress/wSgSize are attacker-controlled from the SG list).

Primitive characterization

  • Write size: up to (sglist_cnt - 17) * sizeof(SCAT_GATH) bytes (= 16 bytes/segment, attacker-controlled content via the SG list).
  • Target: the BUS_DMAMAP allocation (pmap) and the adjacent slab object; the corrupted pAdapter/dma_map pointers are dereferenced later by fOsCommandDone / bus_dmamap_load.

Harness proof

harness.c builds a BUS_DMAMAP-shaped buffer (psg[17] + a canary guard) and replays the loop with sglist_cnt = 64. Output (run.log):

[DF-1284] attacker sglist_cnt=64 (max allowed for psg[]=17)
[DF-1284] overflow past psg[17]: YES (47 guard entries clobbered; 752 bytes of adjacent heap overwritten)

Why not a live in-kernel reproduction (valid hard blocker)

The hptmv driver attaches to HighPoint RR18xx PCI controllers; pciconf -l on the audit guest shows no such device (only virtio + QEMU std-vga). The SIM action handler therefore never runs here. Live trigger conditions: an hptmv-attached target reachable via a CAM pass-through (/dev/passN) device node, with an operator/root credential issuing a CAMIOCOMMAND carrying CAM_SCATTER_VALID and a crafted sglist_cnt. Primitive proven at the object/harness level. Escalation to uid=0 requires the primitive to fire in a running kernel β€” not demonstrable on this guest because the driver never attaches. Honest reported impact: the heap corruption primitive itself.

Fix

fix.diff adds, at the head of the CAM_SCATTER_VALID branch, a rejection of any CCB with ccb->csio.sglist_cnt > MAX_SG_DESCRIPTORS: it logs, frees the command, sets ccb->ccb_h.status = CAM_REQ_TOO_BIG, returns the dmamap, decrements outstandingCommands, calls xpt_done(ccb) and goto Command_Complished β€” exactly mirroring the existing error-cleanup block at :2879-2886. Validated: patch -p1 succeeds; entry.c compiles cleanly under -Werror via the kmod framework (entry.o produced). A structurally-identical unbounded loop exists at entry.c:2524 in fOsBuildSgl (internal command-build path); this fix targets the attacker-reachable CAM path cited by the finding.

Fix-validation status

not_testable for a live before/after (the PoC driver path cannot run on the guest β€” no HighPoint controller). Evidence the fix is correct: (1) harness before/after shows the rejection closes the overflow; (2) entry.c with the fix compiles under -Werror in-tree. (Note: the hptmv module's Makefile has a pre-existing uudecode rule bug that breaks a full make all in this environment independent of the fix; validation was done by compiling entry.o directly.)

Fix verification

not_testable

compile+harness validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (harness). hptmv OsSendCommand sglist_cnt no bounds vs psg[17] -> 752B OOB. No HighPoint controller.