⬢ DragonFlyBSD Kernel Audit
DF-3776 / verdict.json
← back to finding ↓ download raw
{
  "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."
}