DF-3776 / verdict.json
{ "finding_id": "DF-3776", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Mechanism PROVEN on the guest kernel by deterministic replica of if_jme.cs exact callout/serializer protocol; jme-specific runtime hardware-gated (inconclusive per family convention, DF-3715/DF-3738/DF-3760 precedent). jme_tick (sys/dev/netif/jme/if_jme.c:2566-2582) is a CPU0-pinned MP callout whose callback BLOCKS in lwkt_serialize_enter(&sc->jme_serialize) (:2571) and self re-arms at hz (:2579); jme_stop (:2907) and jme_miibus_statchg (:409) tear the callout down with the SYNCHRONOUS callout_stop() while holding ALL driver serializers (ASSERT_IFNET_SERIALIZED_ALL :2898; ifnet_serialize_all at jme_detach :1092, jme_suspend :1589, jme_ioctl :1913). DragonFly callout_stop synchronously waits for an in-progress callback (sys/kern/kern_timeout.c:1091 -> _callout_cancel_or_stop sync=1, ssleep costp at :920). A tick callback dequeued and parked on the serializer therefore deadlocks against the stopper (AB-BA): both threads wedge permanently (replica sysctl thread D5 wchan costp, callout_stop NEVER returned), the driver serializers are never released, and the wedged CPU0 softclock thread stops delivering every callout on CPU0. Replica on the guest: stop under the full serializer array with the jme_tick-shaped handler parked -> callout_stop NEVER returned; fix-shape control (try-enter + 1-tick retry, DF-3776 fix.diff shape) -> callout_stop returned, pending=0.", "exploit_chain": "", "evidence": "run.log: jmereplica[blk]: jme_tick handler BLOCKED on our main serializer (fired=16 ran=15) - entering callout_stop() followed by NOTHING (callout_stop never returned) and 1514 D5 costp sysctl kern.jmereplica.stop_blk=1 in ps output; run.log also shows the fix-shape control jmereplica[try]: callout_stop returned, pending=0; jme_callout_replica.c replicates if_jme.c:2566-2582/:2907/:1092 verbatim (serializer array incl. the skipmain dance); build.log shows -Werror-clean build; fix_build.log shows nativekernel RC=0 with all three jme fixes applied.", "kernel_refs": [ "sys/dev/netif/jme/if_jme.c:772", "sys/dev/netif/jme/if_jme.c:2571", "sys/dev/netif/jme/if_jme.c:2579", "sys/dev/netif/jme/if_jme.c:2881", "sys/dev/netif/jme/if_jme.c:2898", "sys/dev/netif/jme/if_jme.c:2907", "sys/dev/netif/jme/if_jme.c:409", "sys/dev/netif/jme/if_jme.c:1092", "sys/dev/netif/jme/if_jme.c:1589", "sys/dev/netif/jme/if_jme.c:1913", "sys/dev/netif/jme/if_jme.c:2672", "sys/kern/kern_timeout.c:1091", "sys/kern/kern_timeout.c:920", "sys/kern/kern_timeout.c:423" ], "poc_changes": "n/a (source-level finding; replica module written fresh for this pack, adapted from the DF-3760 alc replica to jmes exact protocol: CPU0-pinned callout, blocking enter of sc->jme_serialize, skipmain array dance, stop under the full serializer array; plus a jme_rxeof verbatim-loop replica shared with DF-3777)", "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": 240, "guest_dirty": 0, "build_cmd": "cd /root/kmod && make (jme_callout_replica.ko, -Werror clean)", "run_cmd": "kldload /root/kmod/jme_callout_replica.ko; sysctl kern.jmereplica.arm=1; sleep 2; sysctl kern.jmereplica.stop_try=1; (sysctl kern.jmereplica.stop_blk=1 &); sleep 6; dmesg | grep jmereplica[blk] | tail -2; ps -o pid,stat,wchan,command -ax | grep stop_blk | grep -v grep", "code_hash": "f7952c76d117973c733b4f7867d170a34114040850e256e6f1b6e33d079e5124", "notes": "No JMicron hardware on the guest (vtnet0 lo0 only) but if_jme IS compiled into the running kernel (kldstat -v). Guest was force-killed after the wedge (graceful shutdown impossible by definition of the bug) and reset to the with-src snapshot; post-reset verified clean. fix_status: not_testable (behavior hardware-gated); fix mechanism validated by the stop_try control; fix compile validated (fix_build.log: nativekernel RC=0, 0 warnings, all three jme fixes applied).", "recommended_fix": "jme_tick: replace the blocking lwkt_serialize_enter(&sc->jme_serialize) with lwkt_serialize_try + callout_reset_bycpu(1 tick, JME_TICK_CPUID) retry (family fix, see fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (git-apply clean) switches jme_tick to lwkt_serialize_try + 1-tick retry pinned to JME_TICK_CPUID, mirroring the DF-3715/DF-3738/DF-3760 family fix. Mechanism validated live on the guest: the replicas fix-shape control (identical try-enter + retry protocol) shows callout_stop under the full serializer array returns with pending=0, no deadlock. Compile validated: all three jme fixes applied to /usr/src, incremental nativekernel build of X86_64_GENERIC -> RC=0, zero warnings (fix_build.log). Full driver-level validation is not possible on a guest without JMicron hardware.", "fix_evidence": "run.log: jmereplica[try]: callout_stop returned, pending=0 / RESULT pending=0 (0 == try-shape fully stopped, no deadlock); fix_build.log (17952 lines): >>> Kernel build for X86_64_GENERIC completed RC=0, grep -cE error:|warning: == 0." } |