DF-5636 / verdict.json
{ "finding_id": "DF-5636", "status": "reproduced", "verdict": "smbus_request_bus (sys/bus/smbus/smbconf.c:126-141) returns success without acquiring ownership on two paths: after any wakeup (the loops if (error == 0) break exits without re-checking sc->owner or setting sc->owner = dev) and for SMB_DONTWAIT (the if (error == EWOULDBLOCK) error = 0 fixup converts the fast-fail refusal into a phantom grant). Reproduced on the unmodified production smbus stack (fakebb rig hosting real iicbb/iicbus/iicsmb + in-kernel smbus): a woken waiter returned 0 while sc->owner stayed NULL, a second requester was granted immediately (double grant), SMB_DONTWAIT returned 0 while another device verifiably owned the bus, and a non-owners EACCES release (smbconf.c:177 unconditional wakeup) woke a parked waiter into a success return while the real owner still held the bus. The iicsmb bridge transfer methods perform no arbitration of their own, so a phantom grant puts two kernel threads inside one SMBus transaction (ichsmb KASSERT panic class, interleaved wire transactions elsewhere). fix.diff (re-check after poll, no EWOULDBLOCK-to-success, PCATCH+hz park, validate-before-bridge-release) was applied to /usr/src, the kernel rebuilt and rebooted: all phantom grants gone, DONTWAIT refused with EWOULDBLOCK, woken waiters claim ownership, park killable.", "reproduced": true, "impact": "dos", "confidence": "certain", "exploit_chain": "", "evidence": "run.log F1 lines T(devB): request RETURNED 0 + sc->owner=0 (caller=devB) <<< PHANTOM GRANT and F1.4 double grant; F2 line SMB_DONTWAIT)=0 ... <<< PHANTOM GRANT (must be EWOULDBLOCK); F3B line GRANTED while another device verifiably OWNS the bus; run_fixed.log shows owner=devB claim, EWOULDBLOCK refusals and GOOD on fixed code on kernel #3", "kernel_refs": [ "sys/bus/smbus/smbconf.c:126", "sys/bus/smbus/smbconf.c:131", "sys/bus/smbus/smbconf.c:134", "sys/bus/smbus/smbconf.c:137", "sys/bus/smbus/smbconf.c:96", "sys/bus/smbus/smbconf.c:177", "sys/bus/iicbus/iicsmb.c:250", "sys/bus/iicbus/iicsmb.c:276", "sys/bus/iicbus/iiconf.c:99", "sys/dev/smbus/smb/smb.c:240", "sys/dev/misc/ipmi/ipmi_ssif.c:81", "sys/dev/smbus/cyapa/cyapa.c:1176" ], "poc_changes": "seed sketch rewritten as the family-standard fakebb KLD rig: real production chain (iicbb/iicbus/iicsmb modules + in-kernel smbus), three distinct device_t owner tokens, in-kernel kthreads with started/done handshakes and owner-pointer sampling after every request; two rig iterations (build.sh Makefile copy bug; sys/string.h->bzero; robust cleanup after the DF-5637 teardown wedge; hz/2 release timing so the wakeup, not the 1s poll timeout, drives the fixed-kernel F1)", "attempts": 6, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (baseline) / #3: Fri Oct 9 02:13:57 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (fix validation)", "runtime_sec": 4950, "guest_dirty": 0, "build_cmd": "sh ./build.sh (iicbb_reg.ko + df5636_rig.ko + df5636ctl); fix: patch -p1 < fix.diff in /usr/src && make quickkernel+installkernel KERNCONF=X86_64_GENERIC", "run_cmd": "sh ./run.sh (kldload chain; ./df5636ctl f1|f2|f3|f3b; ./df5636ctl park + kill -9 + unpark)", "code_hash": "0d6a9fb46361a16b9421e6e44b59cf3ff1e8b00f4732809426d00d30d98d4fb6", "notes": "No memory corruption (protocol grants, not bytes) so no uid0 chain; ceiling is mutual-exclusion bypass -> concurrent SMBus transactions (ichsmb KASSERT panic class; interleaved IPMI SSIF/cyapa transfers). Triggers: any contended wakeup (routine on multi-child buses) or SMB_DONTWAIT (/dev/smb nonblocking ioctls, cyapa/atmel O_NONBLOCK opens - root-only devices or kernel threads) => Medium. The baseline run also captured DF-5637s teardown chain live (non-owner release made the real owners later release EACCES, wedging the bus owner). kthread kprintf lines intermittently absent from console (observed quirk); the rig reports the threads sampled verdict from the ioctl thread instead. Guest reset (with-src) performed after the run.", "recommended_fix": "Rewrite the smbus_request_bus ownership loop to re-check sc->owner after every smbus_poll return (continue on wake and on WAIT poll-timeout, unwind the bridge request only on interruption or refused DONTWAIT), set error=0 only when ownership is claimed, keep DONTWAIT EWOULDBLOCK; validate smbus_release_bus ownership BEFORE the parent callback and drop the EACCES-path wakeup; make smbus_poll always PCATCH with an hz bound (see pack fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff applied to /usr/src/sys/bus/smbus/smbconf.c, kernel rebuilt (quickkernel, smbconf.o recompiled) and rebooted (kernel #3): F1 summary 0x3->0x0 with the woken waiter CLAIMING owner and devC parking; F2 0x1->0x0 with SMB_DONTWAIT refused EWOULDBLOCK; F3B 0x1->0x0 no bogus wakeup: GOOD on fixed code; park S2/killable (was D4/unkillable). Baseline behaviors all gone.", "fix_evidence": "run_fixed.log (full fixed-kernel dmesg section) + run.log baseline differential; fix.diff sha256 346fe6893056aecf467e64d12285a0de337461aee150737964c0c3f5f06fbc2a" } |