DF-2308 / verdict.json
{ "finding_id": "DF-2308", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "certain", "verdict": "NOT REPRODUCED -- false positive: the I2CREAD copyout at sys/bus/iicbus/iic.c:347 does use the requested length (s->count) rather than the actual-read length (count from :344), but the claimed disclosure requires an iicbus_read backend that returns success with a short fill, and none exists in-tree. iicbb (iicbb.c:409-421, the only backend on the guest) fills every requested byte unconditionally (while(len) loop, *read=len, return 0; iicbb_readbyte has no failure mode), and pcf (pcf.c:586-640) short-reads only via an error return, which iic.c:345-346 handles by skipping the copyout entirely. Live proof with production modules on the DF-5539 harness rig: 3 runs x 51 success-path I2CREAD attempts at sizes 64..16384, each immediately preceded by the kernel-side M_TEMP groom (kmalloc+fill+kfree of the exact read size, marker DF5558STALEHEAP! + kernel-realm qwords) that DF-5558 used to prove same-size chunk recycling into iic.c kmallocs on this same rig -- every attempt returned rc=0 with pattern_bad=0, marker=no, kptr-qwords=0 (100% of bytes are the slave TX pattern 0xc0+i), i.e. zero bytes of the fresh chunk survive to userspace; all error-path probes returned EINVAL with the 0xAA-prefilled user buffer 100% untouched. The s->count-vs-count mismatch is real latent API misuse worth a hardening one-liner but discloses nothing with any in-tree backend.", "exploit_chain": "", "evidence": "run1.log/run2.log/run3.log: 51/51 success attempts per run, all rc=0 pattern_bad=0 marker=no kptr-qwords=0 plus error probes errno=22 untouched=YES. VERDICT.md: backend-by-backend code trace (iicbb.c:409-421, pcf.c:586-640, iic.c:345-347, iiconf.c:254-259). stock-vs-setup.disasm.txt + env.txt: stock I2CREAD logic under test on production iic.ko (setup fixes DF-5559/5560 disclosed and disasm-verified). panic-setup-df5560-incidental.txt: rig bring-up re-confirmation of sibling DF-5560.", "kernel_refs": [ "sys/bus/iicbus/iic.c:343", "sys/bus/iicbus/iic.c:344", "sys/bus/iicbus/iic.c:345-346", "sys/bus/iicbus/iic.c:347", "sys/bus/iicbus/iicbb.c:409-421", "sys/bus/iicbus/iicbb.c:308-330", "sys/bus/iicbus/pcf/pcf.c:586-640", "sys/bus/iicbus/iiconf.c:254-259" ], "poc_changes": "No seed PoC existed. Authored iicread2308.c from scratch on the DF-5539/DF-5558 production-KLD rig: error-path probes (no-start, after-NACK, wrong-direction) plus groomed/ungroomed success-path I2CREAD at 6 sizes with full-buffer pattern check and marker search. Rig iterations: (1) a stale unpatched iic.ko from an earlier session panicked on the first ioctl (DF-5560 bug, capture kept) -- run.sh now clean-builds and objdump-verifies the setup store before loading; (2) DF patch(1) silently assumes -R on already-applied setup diffs -- switched to source-marker guards; (3) groom KLD build needed Makefile.groom renamed to Makefile and iicbus_if.m copied into the build dir; (4) E2/E3 probe expectations corrected for the harnesss ACK-falling-edge NACK arming (documented in VERDICT.md; kernel error-path invariants unaffected).", "attempts": 4, "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": 2400, "guest_dirty": 0, "build_cmd": "cd /root/df2308 && sh build.sh (harness KLDs via /root/df5539/build.sh; in-tree iicbus.ko/iic.ko with the two disclosed setup patches; groom5558.ko)", "run_cmd": "cd /root/df2308 && sh run.sh", "code_hash": "a226c1020f103d2a0b82c6454ab9e079d7983471444898a50ecd3d05b06889fe", "notes": "Reachability context: /dev/iicN is 0600 root:wheel (iic.c:128-130), so even if a short-read backend existed the oracle would be root-only on default configs. Incidental observations kept as notes: DF-5560 re-confirmed during bring-up; DF patch(1) -R-on-already-applied footgun. Hardening one-liner recorded in recommended_fix; no fix.diff per contract (pure false positive).", "recommended_fix": "Hardening (not a security fix): copy out the actual read length -- error = copyout(buf, s->buf, count); at iic.c:347 -- so a future backend honoring the short-read contract cannot disclose uninitialized tail bytes.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "n/a -- false positive; no fix authored or validated per contract (the premise is unreachable with in-tree backends).", "fix_evidence": "" } |