Stack over-read via strcmp/kprintf on non-NUL-terminated copyinstr result in i_setopt digest handling
Summary
i_setopt uses digsize=6 to copy user-supplied digest algorithm name via copyinstr into 6-byte stack buffer then compares with strcmp against CRC32C. String CRC32C is exactly 6 characters (7 bytes with NUL) so copyinstr copies all 6 bytes WITHOUT NUL terminator returns ENAMETOOLONG. Error guard != EFAULT admits ENAMETOOLONG so strcmp runs on non-NUL-terminated stack buffer reading past hdigest[5]/ddigest[5] into adjacent kernel stack. sdebug %s hdigest also over-reads via kprintf. CRC32C PDU integrity protection never reliably activates. Impact: kernel stack over-read CWE-125 bounded by nearest NUL. Debug builds console/log leak. Root only (/dev/iscsiN 0600). [VERIFIED pass2: reproduced - CRC32C copyinstr copies 6 chars no NUL, strcmp over-reads stack past 6-byte buffer and never matches so negotiation silently broken; digsize 7 fix-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2465 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 306 B | β raw | |
| VERDICT.md | β | 1.4 KB | β raw | |
| build.sh | β | 100 B | view raw | |
| dfmisc.c | β | 12.5 KB | view raw | |
| env.txt | β | 254 B | view raw | |
| fix.diff | β | 628 B | view raw | |
| manifest.json | β | 756 B | view raw | |
| miscctl.c | β | 1.9 KB | view raw | |
| run.log | β | 946 B | view raw | |
| run.sh | β | 100 B | view raw | |
| verdict.json | β | 1.7 KB | view raw |
DF-2465 -- evidence pack
Verdict: reproduced (reproduced=1, impact=none, confidence=certain)
Build: see build.sh (dfmisc.ko + iscsi_initiator.ko)
Run: /root/miscctl isopt
See VERDICT.md for the full narrative, run.log/panic.txt for the decisive output, fix.diff for the post-verification fix.
DF-2465 VERDICT
Status: reproduced Impact: none Confidence: certain
Production i_setopt with headerDigest="CRC32C": copyinstr(6-byte buffer) copies all 6 chars with no NUL and returns ENAMETOOLONG; the != EFAULT guard admits it; strcmp(hdigest, "CRC32C") compares hdigest[6] (adjacent kernel stack, non-NUL) and never matches - demonstrated directly: the production call returned 0 with sess.hdrDigest == NULL, i.e. CRC32C negotiation is silently broken and the strcmp read past the 6-byte stack buffer (the over-read itself). No console leak in the default build (sdebug compiled out), so impact is the functional breakage plus a bounded stack over-read.
Kernel references
sys/dev/disk/iscsi/initiator/isc_subr.c:126sys/dev/disk/iscsi/initiator/isc_subr.c:129sys/dev/disk/iscsi/initiator/isc_subr.c:130
Guest
DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64
Notes
root-only /dev/iscsiN 0600; legacy bucket
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
run.log (production call result: hdrDigest=0, CRC32C rejected)
PoC changes
production i_setopt via iscsi_initiator.ko symbol; opt struct copyind exactly as the iscsictl ioctl path does
Verified recommended fix
use digsize 7 (room for 6 chars + NUL) and reject ENAMETOOLONG explicitly
Verdict
Production i_setopt with headerDigest="CRC32C": copyinstr(6-byte buffer) copies all 6 chars with no NUL and returns ENAMETOOLONG; the != EFAULT guard admits it; strcmp(hdigest, "CRC32C") compares hdigest[6] (adjacent kernel stack, non-NUL) and never matches - demonstrated directly: the production call returned 0 with sess.hdrDigest == NULL, i.e. CRC32C negotiation is silently broken and the strcmp read past the 6-byte stack buffer (the over-read itself). No console leak in the default build (sdebug compiled out), so impact is the functional breakage plus a bounded stack over-read.
No comments yet.