Unbounded scatter/gather copy in btexecuteccb via CAM_SCATTER_VALID CCB (kernel heap overflow)
Summary
When SCSI I/O CCB carries own scatter/gather list (CAM_SCATTER_VALID) btaction() hands csio->sglist_cnt straight to btexecuteccb() as segment count with no bound check. btexecuteccb() copies nseg segments into bccb->sg_list fixed BT_NSEG(32)-entry buffer. Only thing normally caps nseg at 32 is buffer_dmat DMA tag nsegments=BT_NSEG enforced solely inside bus_dmamap_load() but CAM_SCATTER_VALID branch bypasses bus_dmamap_load entirely. CCB via pass(4) with sglist_cnt>32 writes attacker-controlled {addr len} tuples past end of SG slot corrupting adjacent CCBs SG lists overflowing sg_map page into neighboring kernel heap.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2428 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + source trace + exploit-chain stop reason | 2.8 KB | β raw |
| fix.diff | suggested-fix | reject csio->sglist_cnt > BT_NSEG at the CAM_SCATTER_VALID call site (CAM_PROVIDE_FAIL) | 752 B | view raw |
| fix_build.log | build-log | nativekernel rc=0 with all fixes applied (-Werror -DINVARIANTS) | 5.6 MB | β download |
| env.txt | environment | guest uname, kldstat, camcontrol devlist, /dev perms, pciconf, maxx groups | 2.7 KB | view raw |
| build.sh | build-log | documents the HW/permission gate (no PoC binary) | 472 B | view raw |
| run.sh | run-log | prints the gate proof | 461 B | view raw |
DF-2428 β bt.c (buslogic) unbounded scatter/gather copy
Verdict: NOT REPRODUCED (hardware-gated) β source bug CONFIRMED real; fix.diff compiles.
Classification: not_reproduced / HW-gated / impact=none. No BusLogic HBA on this
guest. The SG overflow is real in source; the defense-in-depth fix.diff
compiles cleanly (nativekernel rc=0, -Werror, -DINVARIANTS).
Why not reproduced (the gate)
The bug is in btexecuteccb() of the bt(4) BusLogic SCSI HBA driver
(sys/dev/disk/buslogic/bt.c). The guest has no BusLogic PCI HBA
(pciconf -l: no 0x104b/*) and the bt module is not loaded (kldstat: only
kernel, ehci.ko, xhci.ko). The CAM_SCATTER_VALID path is in a dead SIM. The
only pass0 is on the PIIX3 ata-cam bus (different SIM), so no BusLogic-attached
pass device exists.
Gate proof (this guest):
$ kldstat -> kernel, ehci.ko, xhci.ko (no bt/buslogic) $ pciconf -l -> no BusLogic (0x104b/*) / no SCSI HBA at all $ camcontrol devlist -> only QEMU DVD-ROM (pass0) on PIIX3 ata-cam, not bt
The source bug (real, cited path:line)
sys/dev/disk/buslogic/bt.c:
- #define BT_NSEG 32 (sys/dev/disk/buslogic/btreg.h:40) and each CCB has a
fixed bt_sg_t *sg_list of BT_NSEG entries (allocated BT_NSEG per CCB at
bt.c:832-846).
- In btaction, the CAM_SCATTER_VALID branch casts csio->data_ptr to a segment
array and hands csio->sglist_cnt (a user-controlled u_int16_t) straight to
btexecuteccb(bccb, segs, csio->sglist_cnt, 0) (bt.c:1053-1056) with no bound
check.
- btexecuteccb copies that many segments into bccb->sg_list via
sg = bccb->sg_list; while (dm_segs < end_seg) { sg->len=...; sg->addr=...; sg++; }
(bt.c:1260-1266), where end_seg = dm_segs + nseg (:1257). Any
sglist_cnt > 32 writes attacker-controlled {addr,len} tuples past the end of
the SG slot, corrupting adjacent CCBs / SG lists / neighboring kernel heap.
- The only thing that normally caps nseg at 32 is buffer_dmat DMA tag
nsegments=BT_NSEG (bt.c:618), enforced solely inside bus_dmamap_load();
the CAM_SCATTER_VALID branch bypasses bus_dmamap_load entirely.
Attacker model (per finding): local user issuing CAMIOCOMMAND XPT_SCSI_IO with
CAM_SCATTER_VALID and sglist_cnt > 32 through a /dev/passN on a bt bus.
Exploit chain
Not developed β no BusLogic HBA, hence no bt-attached pass device. Valid hard blocker: the SIM is not instantiated on this guest.
Fix (defense-in-depth, validated to compile)
fix.diff rejects csio->sglist_cnt > BT_NSEG at the CAM_SCATTER_VALID call site
(btfreeccb + CAM_PROVIDE_FAIL + xpt_done). Validated in the combined
nativekernel build (fix_build.log).
Reproduce
No runnable PoC on this guest (no bt HBA). Artifacts: env.txt, fix.diff,
fix_build.log.
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW/permission gated). fix.diff validated to APPLY cleanly and COMPILE in full nativekernel build (rc=0, -Werror, -DINVARIANTS) alongside sibling fixes (findings/poc/DF-2428/fix_build.log).
Compile validation: nativekernel build of patched tree -> 'NK_DONE rc=0'. No runtime test possible (HW/permission gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver code path dead / device node root-only / no malicious device present). No unprivileged->root path.
Evidence (decisive lines)
kldstat -> kernel, ehci.ko, xhci.ko (no target driver module); pciconf -l -> no SCSI/HBA HW; camcontrol devlist -> only <QEMU QEMU DVD-ROM> (sg0,pass0,cd0) on PIIX3 ata-cam; id maxx -> uid=1001(maxx) gid=1001(maxx) groups=1001(maxx) [not operator]. Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2428/{VERDICT.md, fix.diff, manifest.json, build.sh, run.sh, env.txt, fix_build.log}. No PoC source (HW/permission gated).
Verified recommended fix
fix.diff rejects sglist_cnt > BT_NSEG at CAM_SCATTER_VALID call site (CAM_PROVIDE_FAIL). Full git-apply-able diff in findings/poc/DF-2428/fix.diff.
Verdict
NOT REPRODUCED β HW/permission gated on this guest. The bug is REAL in source (traced line-by-line). bt.c (buslogic) unbounded scatter/gather copy; no BusLogic HBA, module not loaded, no bt-attached pass. Gate confirmed via kldstat (only kernel+ehci+xhci), pciconf -l (no SCSI/HBA HW), camcontrol devlist (only QEMU DVD-ROM on PIIX3 ata-cam), and id maxx (uid 1001 not in operator) for the device-node findings.
No comments yet.