DF-5679 / verdict.json
{ "finding_id": "DF-5679", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "iscsi_crc32()/iscsi_crc32_ext() accept size_t size but calculate_crc32c() narrows it to unsigned int (sys/libkern/icrc32.c:763, called at :779/:785), so a 0x100000009-byte request is silently serviced as 9 bytes: demonstrated on the guest (T10 and fixdemo_baseline: identical CRCs 4520c30c, silent return). This is an under-read (unchecked tail), not an overflow. Bounds audit of every in-tree caller (kern_dmsg DMSG_AUX_MAX clamp at kern_dmsg.c:371-376, swap_pager MAXPHYS, hammer2 64KB radix cap, ext2fs block sizes, sizeof-based hashes) shows no caller can reach 2^32, so impact is a correctness-contract / defense-in-depth hazard only. The rest of the file is proven bit-exact and memory-safe by the same rig: 21/21 differential checks (all 9 tables element-wise vs generated references, KATs 0xCBF43926/0xE3069283, every length 0..600 x alignment 0..3 for both engines, chaining semantics incl. by-1/by-3 chunk churn, 1MiB random, PROT_NONE guard-page OOB probe with zero faults across 256 boundary lengths).", "exploit_chain": "", "evidence": "run.log: T10 truncation: iscsi_crc32(buf, 0x100000009)=4520c30c iscsi_crc32(buf, 9)=4520c30c TRUNCATED (equal) and RESULT: ALL-PASS (21 checks, 0 failures); fix_run.log: baseline FIX-BAD ... truncation still present (exit 1) vs patched FIX-OK: length honored -> child faulted on guard page (sig 11) (exit 0); VERDICT.md for the full class-by-class kill list with path:line.", "kernel_refs": [ "sys/libkern/icrc32.c:760", "sys/libkern/icrc32.c:763", "sys/libkern/icrc32.c:765", "sys/libkern/icrc32.c:776", "sys/libkern/icrc32.c:779", "sys/libkern/icrc32.c:783", "sys/libkern/icrc32.c:785", "sys/sys/systm.h:205", "sys/kern/kern_dmsg.c:371", "sys/vfs/hammer2/hammer2.h:1457" ], "poc_changes": "No seed existed (orchestrator reserved IDs only); rig authored from scratch. Three iterations: (1) fix.diff was missing the #ifndef _KERNEL prototype hunk at icrc32.c:15-16 (patched-copy build failed with conflicting types) and one harness CHECK had a ^/== precedence bug caught by -Wparentheses; (2) first runs single T7 failure was my harness double-processing the final 3-byte chunk (228=76*3 leaves no remainder) - kernel exonerated; (3) clean ALL-PASS run + fix validation.", "attempts": 3, "guest_uname": "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", "runtime_sec": 1, "guest_dirty": 0, "build_cmd": "sh build.sh (guest, in pack dir: copies /usr/src/sys/libkern/{icrc32,crc32}.c, cc -O2 -Wall -o icrc32_diff icrc32_diff.c; cc -O2 -Wall -o fixdemo_baseline fixdemo.c icrc32.c; patch -d fixwork -p1 < fix.diff && cc -O2 -Wall -o fixdemo_patched fixdemo.c fixwork/sys/libkern/icrc32.c)", "run_cmd": "sh run.sh (guest, in pack dir: ./icrc32_diff; ./fixdemo_baseline; ./fixdemo_patched)", "code_hash": "29acbcd0a567468761346edc7a6a3ebdb0ab1b59843a3a4adec315dc6bad8386", "notes": "Decisive harness run measured 0.06s real (rounded up to 1s). Guest /usr/src/sys/libkern/icrc32.c md5 29e2ead0a146caec695a6804944e3cde == audited tree; running kernel built from it Jul 2 2026. Guest left up, artifacts in /tmp/DF-5679 only, no panic, no reset needed. Negative result has weight: guard-page probe proves no OOB read in the init_bytes/running_length/end_bytes walk; all 9 CRC tables verified element-wise. Reserved IDs DF-5680..DF-5691 unused (no further findings).", "recommended_fix": "Propagate size_t end-to-end: calculate_crc32c/multitable_crc32c/crc32c_sb8_64_bit length params and the icrc32.c:15 + sys/sys/systm.h:207 prototypes take size_t (see fix.diff); keeps parity with crc32()/crc32_ext() which already honor size_t.", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff (size_t end-to-end, 4+1 hunks) applied to a copy of the pristine source and rebuilt on the guest: baseline fixdemo silently returns the 9-byte CRC (truncation present, exit 1); patched fixdemo honors the full 0x100000009 length and the child faults on the PROT_NONE guard page (SIGSEGV, FIX-OK, exit 0). The silently-truncated behavior is gone. Kernel-rebuild validation not performed: userland rig, non-memory-corruption Info finding (mandatory kernel rebuild rule applies to memory-corruption findings only).", "fix_evidence": "fix_run.log (baseline FIX-BAD/exit 1 vs patched FIX-OK sig 11/exit 0); fix_build.log (patch -p1 hunks 1-4 + systm.h hunk applied, both builds clean); fix.diff (sha256 fed7a77746635dc8bf11fd3a3301d51fee995ff6db31537dd7ad68796a029027)." } |