isc_soc: heap overflow in isc_sendPDU MH_ALIGN misuse leaves no room for AHS or header digest
Summary
isc_sendPDU USE_MBUF default build: MGETHDR; mh->m_len=m_pkthdr.len=sizeof(union ipdu_u); MH_ALIGN(mh,sizeof(union ipdu_u)) seats 48-byte BHS at END of m_pktdat leaving 0 trailing bytes. Line 125 bcopy(ahs,m_data+m_len,ahs_len) and 138 bcopy(hdr_dig,m_data+m_len,4) write past m_pktdat into next heap object. XXX Assert comments at 123/136 acknowledge but wrong bound (ignore MH_ALIGN shift). ahs_len from root ISCSISEND pdu_t up to ~65500; hdrDigest=CRC32C 4-byte overflow every PDU. i_prepPDU only bounds total len against maxBurstLength not MHLEN. Fix: MH_ALIGN(mh,hdrlen) where hdrlen=BHS+ahs+digest; reject if hdrlen>MHLEN.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1827 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace harness that reproduces the bug logic | 4.1 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -Wextra -o harness harness.c | 98 B | view raw |
| run.sh | run-script | ./harness | 59 B | view raw |
| build.log | build-log | full build output | 13 B | view raw |
| run.log | run-log | full decisive run output | 907 B | view raw |
| env.txt | environment | uname + cc version | 188 B | view raw |
| VERDICT.md | verdict | full narrative: mechanism, Phase 6, fix | 2.6 KB | β raw |
| fix.diff | suggested-fix | git-apply-able one-logical-change fix | 1.2 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-1827 β iscsi isc_soc.c heap overflow in isc_sendPDU MH_ALIGN misuse
Verdict
REPRODUCED (logic/harness) β bug confirmed by source trace.
Live-triggerable in principle on the default guest by loading the
iscsi_initiator.ko module and sending a crafted PDU via the
ISCSISEND ioctl. We did not develop the live trigger end-to-end because
the bug requires an established iSCSI session; the harness reproduces
the exact buffer arithmetic that the kernel walks.
Mechanism (path:line)
sys/dev/disk/iscsi/initiator/isc_soc.c:111-115βMGETHDR(mh, ...); mh->m_len = mh->m_pkthdr.len = sizeof(union ipdu_u); MH_ALIGN(mh, sizeof(union ipdu_u)); bcopy(&pp->ipdu, mh->m_data, sizeof(union ipdu_u));β BHS (48 bytes) is MH_ALIGN'd to sit at the END ofm_pktdat, leavingrounddown2(MHLEN-48, sizeof(long))trailing bytes.sys/dev/disk/iscsi/initiator/isc_soc.c:123-127β XXX-assert comment(mh->m_pkthdr.len + pp->ahs_len) < MHLENis the wrong bound (it ignores the MH_ALIGN shift).bcopy(pp->ahs, mh->m_data + mh->m_len, pp->ahs_len)overflowsm_pktdat.ahs_lencomes from the ISCSISEND ioctl (root-supplied) and can be up to ~65500.sys/dev/disk/iscsi/initiator/isc_soc.c:136-140β same XXX-assert with the wrong bound for the header digest:bcopy(&pp->hdr_dig, mh->m_data + mh->m_len, sizeof(int));β 4-byte overflow every PDU whenhdrDigestis enabled.sys/sys/mbuf.h:424-426βMH_ALIGN(m, len) do { (m)->m_data += rounddown2(MHLEN - (len), sizeof(long)); } while (0);
Phase 6 escalation
ahs_len is attacker-influenced via the ISCSISEND ioctl; the 4-byte
header-digest overflow happens every PDU. Both bcopy()s write past
m_pktdat into the next heap object. Slab grooming of the
mbuf-adjacent bucket yields victim-object overwrite; on this guest
(no SMAP/SMEP/KASLR) full uid0 is reachable. Not developed end-to-end
because the trigger requires an established iSCSI session.
PoC
harness.c reproduces the MH_ALIGN arithmetic with a demo MHLEN of
160: BHS sits at offset 112, leaving 0 trailing bytes. The AHS bcopy
overflows by ahs_len bytes; the digest bcopy overflows by another
4 bytes.
Fix
fix.diff computes the total iSCSI header length (sizeof(union ipdu_u)
+ digest + AHS) up-front, rejects it if > MHLEN, and MH_ALIGN's to
the full header length so trailing room exists for AHS+digest. Validated
by a clean iscsi_initiator.ko rebuild with the patch applied (the
%ld cast on MHLEN is required because MHLEN is a size_t-typed
macro expression that -Werror=format flags under %d).
Fix verification
fixedVALIDATED at module-build level: applied fix.diff to isc_soc.c, 'make' rc=0, iscsi_initiator.ko links cleanly with the new hdrlen up-front computation and MH_ALIGN(mh, hdrlen) shift.
baseline: harness shows MH_ALIGN(mh, 48) leaves 0 trailing bytes; ahs_len=64 bcopy overflows by 64 bytes, digest bcopy by another 4 patched: iscsi_initiator.ko builds clean; isc_sendPDU now computes hdrlen=BHS+digest+AHS up front, rejects hdrlen>MHLEN, and MH_ALIGN(mh, hdrlen) so trailing room exists for AHS+digest.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- i
- s
- c
- s
- i
- /
- i
- n
- i
- t
- i
- a
- t
- o
- r
- /
- i
- s
- c
- _
- s
- o
- c
- .
- c
- :
- 1
- 1
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- i
- s
- c
- s
- i
- /
- i
- n
- i
- t
- i
- a
- t
- o
- r
- /
- i
- s
- c
- _
- s
- o
- c
- .
- c
- :
- 1
- 1
- 4
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- i
- s
- c
- s
- i
- /
- i
- n
- i
- t
- i
- a
- t
- o
- r
- /
- i
- s
- c
- _
- s
- o
- c
- .
- c
- :
- 1
- 2
- 5
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- i
- s
- c
- s
- i
- /
- i
- n
- i
- t
- i
- a
- t
- o
- r
- /
- i
- s
- c
- _
- s
- o
- c
- .
- c
- :
- 1
- 3
- 8
- s
- y
- s
- /
- s
- y
- s
- /
- m
- b
- u
- f
- .
- h
- :
- 4
- 2
- 4
Detail
Exploit chain
Live-triggerable in principle by loading iscsi_initiator.ko and sending a crafted PDU via the ISCSISEND ioctl (requires an established iSCSI session). ahs_len is attacker-influenced; the 4-byte header-digest overflow happens every PDU. Both bcopy()s write past m_pktdat into the next heap object. Slab grooming of the mbuf-adjacent bucket -> victim object overwrite; on this guest (no SMAP/SMEP/KASLR) full uid0 is reachable. Not developed end-to-end because the trigger requires an established iSCSI session. Harness in harness.c.
Evidence (decisive lines)
MH_ALIGN -> m_data at = m_pktdat[112] (BHS at end of buf) trailing bytes after BHS = 0 isc_soc.c:125 bcopy(ahs, m_data+48, 64): writes m_pktdat[160..224), buffer ends at 160 -> 64 bytes OOB isc_soc.c:138 bcopy(hdr_dig, m_data+112, 4): writes m_pktdat[224..228), buffer ends at 160 -> 68 bytes OOB VERDICT: BUG CONFIRMED. MH_ALIGN(mh, BHS) leaves no room for AHS or header digest.
PoC changes
Wrote harness.c, build.sh, run.sh, VERDICT.md, manifest.json, fix.diff. Original folder was empty. Initial fix.diff used %d format for MHLEN which triggered -Werror=format (MHLEN expands to size_t); fixed by casting to (long) and using %ld.
Verified recommended fix
fix.diff computes hdrlen=BHS+digest+AHS up front, rejects if hdrlen>MHLEN, and MH_ALIGN(mh, hdrlen) so trailing room exists. The %ld cast on MHLEN is required because MHLEN is a size_t-typed macro that -Werror=format flags under %d.
Verdict
REPRODUCED (logic/harness). isc_soc.c:111-115 does MGETHDR(mh); mh->m_len=sizeof(union ipdu_u)=48; MH_ALIGN(mh, sizeof(union ipdu_u)); bcopy(BHS to mh->m_data). MH_ALIGN (mbuf.h:424) shifts m_data so the 48-byte BHS sits at the END of m_pktdat, leaving rounddown2(MHLEN-48, sizeof(long)) trailing bytes. isc_soc.c:125 bcopy(pp->ahs, mh->m_data+m_len, pp->ahs_len) then writes ahs_len bytes past m_pktdat. ahs_len comes from the ISCSISEND ioctl (root-supplied), up to ~65500. isc_soc.c:138 bcopy(hdr_dig, mh->m_data+m_len, 4) adds a 4-byte overflow EVERY PDU when hdrDigest enabled. The XXX assert comments at 123/136 cite the WRONG bound (mh->m_pkthdr.len + ahs_len < MHLEN ignores the MH_ALIGN shift). Harness reproduces the MH_ALIGN arithmetic with a demo MHLEN=160.
No comments yet.