DF-4341 / verdict.json
{ "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)" } |