⬢ DragonFlyBSD Kernel Audit
DF-4341 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4341",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Could not be exercised on the audit guest: the mn(4) driver requires the Siemens Easy321-R1 Munich32X+FALC54 PCI card (0x110a:2101), which QEMU does not emulate; if_mn is optional mn netgraph, absent from GENERIC, and if_mn.ko is not built (kldload -> ENOENT), so the mn netgraph node type can never register (ng_newtype runs only in mn_attach). The finding itself is statically certain: mn_intr is registered INTR_MPSAFE with a NULL serializer (if_mn.c:1329), the netgraph core invokes ngmn_rcvdata synchronously in sender threads with no lock (ng_base.c:1678-1697) and ngmn_disconnect in teardown threads with no lock (ng_base.c:839-858), and ngmn_disconnect (if_mn.c:779-794) frees the exact descriptor rings and mbufs that mn_rx_intr/mn_tx_intr walk (if_mn.c:1075-1090, 1117-1150) behind a bare TOCTOU state check -- plus it frees RX rings even when the 30us channel-zap handshake failed (if_mn.c:773-784), leaving the device DMA-ing into freed memory with no race required. Net effect on real hardware: UAF writes (if_mn.c:1122 via reused trxd->m), UAF on the core-freed hook (if_mn.c:1125), descriptor double-free onto mn_desc_free, and E1-payload hardware writes into freed mbuf clusters. Classification inconclusive (not not_reproduced) because nothing was refuted -- the surface simply cannot exist on this guest.",
  "exploit_chain": "on real hardware: root ngctl loop { mkpeer mn0 ksocket ts2 ...; rmhook mn0 ts2 } while E1 traffic drives RX/TX interrupts -> mn_rx_intr/tx_intr race ngmn_disconnect ring teardown -> freed trxd reused via mn_desc_free freelist -> cross-channel write through stale dp->m (if_mn.c:1122) / double-free -> M_MN pool corruption -> panic or freelist-alias arbitrary-write primitive. Not executable on this guest (no device).",
  "evidence": "VERDICT.md (race-window table + sinks); env_guest.txt + run.log (guest: no 110a PCI device, no if_mn.ko, optional-not-GENERIC); fix.diff (serializer wiring + polled zap, git apply --check OK); README.md full write-up",
  "kernel_refs": [
    "sys/dev/netif/mn/if_mn.c:1329",
    "sys/dev/netif/mn/if_mn.c:1074",
    "sys/dev/netif/mn/if_mn.c:1109",
    "sys/dev/netif/mn/if_mn.c:1117",
    "sys/dev/netif/mn/if_mn.c:1122",
    "sys/dev/netif/mn/if_mn.c:1125",
    "sys/dev/netif/mn/if_mn.c:743",
    "sys/dev/netif/mn/if_mn.c:758",
    "sys/dev/netif/mn/if_mn.c:773",
    "sys/dev/netif/mn/if_mn.c:779",
    "sys/dev/netif/mn/if_mn.c:787",
    "sys/dev/netif/mn/if_mn.c:599",
    "sys/dev/netif/mn/if_mn.c:620",
    "sys/dev/netif/mn/if_mn.c:483",
    "sys/netgraph/netgraph/ng_base.c:1678",
    "sys/netgraph/netgraph/ng_base.c:839",
    "sys/netgraph/netgraph/ng_base.c:1946"
  ],
  "poc_changes": "trigger is an ngctl connect/rmhook loop written fresh for this pack (no seed); guest-mode runner documents surface absence",
  "attempts": 1,
  "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": 42,
  "guest_dirty": 0,
  "build_cmd": "n/a (kernel driver, optional mn netgraph; not buildable/loadable on guest without device)",
  "run_cmd": "./run.sh guest  (executed over vm.sh run_root; see run.log)",
  "code_hash": "",
  "notes": "Hardware-gated (Dillon legacy-tier hardware driver). Guest evidence: pciconf empty for 0x110a; /boot/kernel/if_mn.ko missing; kldload ENOENT; conf/files marks driver optional. Fix validation not possible on this guest (no baseline can be established without the card).",
  "recommended_fix": "Register a lwkt serializer with bus_setup_intr and hold it across all ngmn_* methods; poll the channel-zap handshake in ngmn_disconnect and refuse to free rings on failure (fix.diff in this pack)",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "No baseline can be established on a guest without the Munich32X hardware; fix.diff is line-accurate against sys/dev/netif/mn/if_mn.c (git apply --check clean) and covers every cited sink: serializer shared by ISR and all ngmn_* methods; polled zap with leak-on-failure instead of free-under-armed-device.",
  "fix_evidence": "findings/poc/DF-4341/fix.diff (git apply --check OK on the read-only tree)"
}