Scatter/gather list copy has no bounds vs the 32-entry hardware SG buffer
Summary
trm_ExecuteSRB at trm.c:434-440: copy loop psg++ dm_segs++ for nseg entries into pSRBSGL (DMA buffer of TRM_MAX_SG_LISTENTRY=32 entries, trm.h:92). DMA tag buffer_dmat nsegments=TRM_NSEG=33 (trm.h:98 btoc(MAXPHYS)+1) -> 33 segments legitimate -> 8B overflow. pass(4) CAM_SCATTER_VALID passes pcsio->sglist_cnt (u_int16_t 0-65535) as nseg at :666-669 with NO cap -> unbounded heap overflow of DMA-coherent SG allocation. Fix: check nseg<=TRM_MAX_SG_LISTENTRY, change buffer_dmat nsegments to 32, validate sglist_cnt.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1237 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full path:line trace, threat model, fix rationale | 5.0 KB | β raw |
| README.md | readme | claim, verdict, runnable-PoC instructions | 2.4 KB | β raw |
| trm_sg_overflow.c | trigger-source | PoC: reachability check (kldstat grep trm) | 2.3 KB | view raw |
| build.sh | build-script | cc -O -Wall -o trm_sg_overflow trm_sg_overflow.c | 188 B | view raw |
| run.sh | run-script | ./trm_sg_overflow | 102 B | view raw |
| build.log | build-log | PoC build, full output | 124 B | view raw |
| run.log | run-log | PoC run on this guest (trm not loaded) | 508 B | view raw |
| fix.diff | suggested-fix | reject nseg > TRM_MAX_SG_LISTENTRY in trm_ExecuteSRB; change buffer_dmat nsegments TRM_NSEG -> TRM_MAX_SG_LISTENTRY | 1.1 KB | view raw |
| fix_build.log | build-log | kernel build rc=0 with all 5 fixes applied; trm.c compiled clean as trm.ko under -Werror | 2.1 KB | view raw |
| env.txt | environment | guest uname, cc version, PCI topology | 2.0 KB | view 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-1237 β README
Finding
trm_ExecuteSRB at sys/dev/disk/trm/trm.c:434-440 copies nseg
scatter/gather entries into pSRB->pSRBSGL (a DMA-coherent buffer of
TRM_MAX_SG_LISTENTRY=32 entries β trm.h:92, trm.c:3498) with no
bounds check. Two paths reach it:
CAM_SCATTER_VALIDattrm.c:666-669passespcsio->sglist_cnt(au_int16_t, 0..65535) straight from userspace β up to 65535 Γ 8B = ~512 KB DMA-coherent heap overflow.bus_dmamap_loadcallback attrm.c:625:buffer_dmatis created withnsegments=TRM_NSEG=33(trm.h:98,trm.c:3429) on x86_64 (MAXPHYS=128KB β btoc=32 β TRM_NSEG=33) β legitimate 33rd segment overflows by 1 entry (8 bytes).
Verdict
NOT REPRODUCED on this guest: trm is not in X86_64_GENERIC
(sys/config/X86_64_GENERIC has no device trm) and no Tekram DC-395
controller is present in pciconf -lv. trm_ExecuteSRB is dead code on
this kernel. PoC prints the reachability status.
Confidence (bug is real): certain β traced line-by-line in sys/.
Impact ceiling: operator-group β kernel heap corruption via DMA
coherent slab overflow, on any host with a Tekram DC-395U/UW adapter and
the trm module loaded.
How to reproduce
./build.sh && ./run.sh
Expected on this guest: PoC builds clean, prints "trm driver NOT loaded
(no 'device trm' in X86_64_GENERIC)" and "trm_ExecuteSRB is dead code on
this kernel". On a host with the trm module loaded and a Tekram
adapter, pass(4) CAMIOCOMMAND XPT_SCSI_IO with
CAM_SCATTER_VALID and sglist_cnt > 32 overflows pSRBSGL.
Files
| Path | Purpose |
|---|---|
trm_sg_overflow.c |
PoC: reachability check (kldstat grep trm) |
build.sh / run.sh |
exact build/run commands |
fix.diff |
reject nseg > TRM_MAX_SG_LISTENTRY; change buffer_dmat nsegments TRM_NSEP -> TRM_MAX_SG_LISTENTRY |
VERDICT.md |
full path:line trace, threat model, fix rationale |
build.log / run.log |
PoC build + run outputs |
fix_build.log |
module-build compile validation of fix.diff |
env.txt |
guest uname / cc / device topology |
DF-1237 β VERDICT
Finding: trm_ExecuteSRB at sys/dev/disk/trm/trm.c:434-440 copies
nseg scatter/gather entries into pSRB->pSRBSGL (a DMA-coherent
allocation of TRM_MAX_SG_LISTENTRY=32 entries) with no bounds check.
Two paths feed nseg:
trm_actionCAM_SCATTER_VALIDbranch attrm.c:666-669passespcsio->sglist_cnt(au_int16_t, 0..65535) verbatim β fully user-controlled.- The
bus_dmamap_loadcallback path attrm.c:625is bounded bybuffer_dmat'snsegments=TRM_NSEG=btoc(MAXPHYS)+1β but on x86_64MAXPHYS = 128 KB, soTRM_NSEG = 33whilepSRBSGLhas only 32 slots β 1-entry (8-byte) overflow even via the legitimate path.
Status: NOT REPRODUCED on this guest β trm is not in
X86_64_GENERIC and no Tekram DC-395 controller is present.
Confidence (bug is real): certain (traced line-by-line in sys/).
Impact ceiling: unbounded DMA-coherent heap overflow (8 bytes Γ
user-supplied nseg) β exploitable by any local user with write access
to /dev/passN (operator group) once a Tekram adapter and trm module
are present.
Mechanism (confirmed line-by-line in sys/)
-
pSRBSGLis allocated atsys/dev/disk/trm/trm.c:3027frompACB->sg_dmat, which is created attrm.c:3498withc /*maxsize*/ TRM_MAX_SG_LISTENTRY * sizeof(SGentry),TRM_MAX_SG_LISTENTRY = 32(trm.h:92),sizeof(SGentry) = 8(trm.h:80-83). SopSRBSGLholds exactly 32 entries. -
The copy loop in
trm_ExecuteSRB(trm.c:431-440): ```c if (nseg != 0) { PSEG psg; bus_dma_segment_t *end_seg; bus_dmasync_op_t op;end_seg = dm_segs + nseg; psg = pSRB->pSRBSGL; while (dm_segs < end_seg) { psg->address = dm_segs->ds_addr; psg->length = (u_long)dm_segs->ds_len; totalxferlen += dm_segs->ds_len; psg++; dm_segs++; }
`` **No bounds check onnseg.** Each iteration writes 8 bytes past the previouspsg, sonseg > 32` overflows the DMA-coherent slab. -
nsegsources: -bus_dmamap_loadcallback (trm_actionCAM_DIR_IN/OUTnon-scatter path,trm.c:620-627).buffer_dmatis created withc /*nsegments*/ TRM_NSEG, /* trm.c:3429 */whereTRM_NSEG = btoc(MAXPHYS) + 1(trm.h:98). On x86_64MAXPHYS = 128*1024,btoc(128K) = 32, soTRM_NSEG = 33. bus_dma can legitimately produce up to 33 segments for a 128 KB buffer that straddles 33 pages β 1 entry (8 bytes) more thanpSRBSGLcan hold. Off-by-one overflow even withoutCAM_SCATTER_VALID. -CAM_SCATTER_VALID(trm.c:666-669):c segs = (struct bus_dma_segment *) pcsio->data_ptr; trm_ExecuteSRB(pSRB, segs, pcsio->sglist_cnt, 1);pcsio->sglist_cntis au_int16_tfrom userspace.pass(4)honorsCAM_SCATTER_VALIDand lets the caller supply a complete SG list. No bounds check. A caller passingsglist_cnt=65535triggers an 8 Γ 65535 = 512 KB DMA-coherent overflow. -
The overflow corrupts whatever follows
pSRBSGLin the DMA-coherent segment pool β adjacent SRBs'pSRBSGL/pNextSRB/pccbpointers, depending on slab layout. On any host with a Tekram adapter, this is a real operator-group β kernel heap corruption vector.
Why it is NOT REPRODUCED on this guest
sys/config/X86_64_GENERICdoes not containdevice trm. The driver is built only as the loadable moduletrm.ko.- The audit guest has no Tekram DC-395U/UW controller in
pciconf -lv. - PoC
trm_sg_overflow.cconfirmskldstat -v | grep trmreturns empty.
Threat model & privilege boundary
Same as DF-1235: /dev/passN is operator-group on stock devfs; any user
in operator can issue CAMIOCOMMAND with XPT_SCSI_IO,
CAM_SCATTER_VALID, and a forged ccb_scsiio.sglist_cnt. Loading the
trm module is a one-time admin action; the exploit itself is fully
unprivileged once the module is loaded.
Fix (authored in fix.diff, applied + compile-validated)
Two-part fix:
-
Reject
nseg > TRM_MAX_SG_LISTENTRYat the top oftrm_ExecuteSRB's SG-copy block (trm.c:431-440), returningCAM_REQ_TOO_BIGand the SRB to the free list (mirrors existing failure paths in the file). This closes theCAM_SCATTER_VALIDuser-controlled overflow. -
Change
buffer_dmat'snsegmentsfromTRM_NSEG(33) toTRM_MAX_SG_LISTENTRY(32) attrm.c:3429, sobus_dmamap_loadcannot produce a 33rd segment that overflows by one entry. With both fixes, the SRB's 32-entry SG buffer cannot be overrun on any path.
Validation
fix.diffapplies cleanly withpatch -p1 --forward(verified).- All 5 audit fixes applied together;
make -j6 nativekernel KERNCONF=X86_64_GENERICreturned rc=0 with no errors / warnings under-Werror.trm.cwas compiled cleanly as the loadable moduletrm.ko. - Fix is not_testable at runtime on this guest (trm not in GENERIC, no Tekram HW).
Fix verification
not_testablecompile validated
nativekernel rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. trm SG list no bounds vs [32] -> DMA heap overflow. trm NOT in GENERIC, no Tekram HW.
No comments yet.