{
  "finding_id": "DF-2233",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "The lockless traversal UAF is real and reproduces with stock modules: two independent boots, each panicking in under 45 seconds with Stopped at iconv_lookupconv+0x30 movq 0x800(%rsi),%rdi (fault VA 0x800) while a plain kern.iconv.add caller walked the converter list and kldunload libiconv concurrently freed and NULLed the class ops tables. A third rig (in-kernel iconv_open walkers vs unload) killed the guest outright with no trap text. The cslist-walk variant against register/free churn performs stale reads of freed but still-mapped chunks and does not fault. Reachability is root-on-both-sides on default config: sysctl writes are gated by SYSCAP_NOSYSCTL_WR and kldunload needs root, with an unprivileged mount-side angle only under vfs.usermount=1. Impact is therefore local DoS / kernel UAF read, severity Medium as filed. The fix (lock held across traversal and dispatch, plus an in-flight refcount drain guard on unload) was validated on rebuilt modules: the baseline panic signature never reappears, 316M-walk soaks and 240 s unload storms are clean; a nanosecond-scale kldunload-vs-inflight residual remains and is documented (needs linker-level drain support).",
  "exploit_chain": "",
  "evidence": "panic.txt and panic.2.txt: serial captures of both panics showing Stopped at iconv_lookupconv+0x30 with fault virtual address 0x800 from process churnadd. run.log: full rig narrative including the 300M-walk churn run (no fault, stale-read analysis), the refcount-leak side observation (pair ref=12685 never drains) and the silent hard death of the bundled-module rig. fixvalidation.log: fixed-build soaks A, B2 (316798072 walks, clean), C3 (240 s unload storm, clean) and the documented C4 residual.",
  "kernel_refs": [
    "sys/libiconv/iconv.c:145",
    "sys/libiconv/iconv.c:150",
    "sys/libiconv/iconv.c:162",
    "sys/libiconv/iconv.c:167",
    "sys/libiconv/iconv.c:243",
    "sys/libiconv/iconv.c:251",
    "sys/libiconv/iconv.c:418",
    "sys/libiconv/iconv.c:133",
    "sys/libiconv/iconv.c:218",
    "sys/libiconv/iconv.c:80",
    "sys/kern/subr_kobj.c:225",
    "sys/kern/kern_sysctl.c:1447"
  ],
  "poc_changes": "No seed pack existed; built from scratch. Rig 1 needed no custom module at all (stock libiconv.ko + stock kern.iconv.add sysctl path driven by churnadd walk mode). The bundled race2233.ko compiles the pristine libiconv sources plus an in-kernel walk harness replicating the mount-path iconv_open call. CTLFLAG_NOLOCK was required on the harness sysctl so several hammer processes run concurrently.",
  "attempts": 6,
  "guest_uname": "DragonFly dfbsd 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": 2100,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh (guest root): cd /root/rig2233 && make; cc -O -o churnadd churnadd.c",
  "run_cmd": "sh run.sh (guest root): kldload libiconv; 4x /root/rig2233/churnadd walk 200000000 & loop { kldunload libiconv; kldload libiconv }",
  "code_hash": "cd8f3075935550cd438e0618c0b011569527e6d98229140b4b53101035da0b36",
  "notes": "Side observation for upstream: after heavy successful open/close churn the UTF-16BE<->UTF-8 pair cp_refcount leaks monotonically (observed 12685) making the module permanently unloadable. Fix validation A/B: baseline panics in iconv_lookupconv+0x30 twice; with fix.diff applied and modules rebuilt, identical and harder soaks never reproduce that signature; residual teardown micro-race documented in VERDICT.md.",
  "fix_status": "fixed",
  "fix_notes": "fix.diff: rename lookups to _locked variants, hold iconv_lock (shared or exclusive) across traversal AND dispatch in iconv_open, iconv_add and iconv_sysctl_add (lookup moved under the exclusive section), and add an iconv_refs in-flight counter with EBUSY drain guard in iconv_mod_unload. Validated on rebuilt modules; the walk UAF is gone. Residual: an in-flight caller whose ref lands after the final unload check can still race the unmap (generic DragonFly kldunload limitation, one silent reboot observed after a max-aggression supervised storm); airtight closure needs linker-level drain support.",
  "recommended_fix": "Hold iconv_lock across all list traversals and result dispatch; add an in-flight refcount drain guard refusing unload (EBUSY) while callers are inside the module."
}
