# DF-2615 — kdmsg_iocom_uninit leaks the queued LNK_PING

## What was verified

**Baseline (instrumented kernel, fix NOT applied): exactly one leaked
kdmsg_msg per mount/umount cycle, 20/20 cycles.** Instrumentation
(instrument.diff, kprintf counters inside kdmsg_iocom_uninit on an
otherwise-stock kernel) shows per cycle:

    DF2615_DBG: uninit entered rd_td=0 wr_td=0 flags=00000003
    DF2615_DBG: after ping write, msgq.first=0xfffff801168ef4c0
    DF2615_DBG: uninit exit msgq_nleak=1
    DF2615_LEAK: 1 kdmsg_msg(s) queued at iocom_uninit exit

i.e. no iocom threads exist (rd_td=wr_td=0), EXITNOACC (0x8000) is clear
in flags, the LNK_PING allocated at kern_dmsg.c:280-281 is TAILQ_INSERTed
onto iocom->msgq by kdmsg_msg_write_locked (kern_dmsg.c:2018-2021), and
nothing drains it before hmp (with the embedded iocom) is freed —
kdmsg_iocom_uninit (kern_dmsg.c:264-317) only joins threads, drops
freerd/freewr_state caches and msg_fp.

**Precondition refined (important correction to the filed text):** the
leak requires the mount to have been made with `cluster_fd < 0` (no
iocom threads ever created).  Stock `mount_hammer2(8)` normally
auto-starts the hammer2 service daemon (`system("/sbin/hammer2 -q
service")`, mount_hammer2.c:225) and connects to it (cluster_fd >= 0) —
in that configuration the write thread exists, sets EXITNOACC on exit,
and the PING self-drains at write time (verified live: 0 leak lines via
plain `mount -t hammer2`).  The leak path is taken whenever the mount
proceeds without a cluster connection: daemon not running / connect
failed (mount_hammer2 passes cluster_fd=-1 and still mounts,
mount_hammer2.c:162-167), or any direct mount(2) caller — reproduced
with `df2615_trigger.c` (mount(2) with cluster_fd=-1, 20 cycles).
The finding's mechanism is fully confirmed; its "default local case"
wording overstates how often stock mounts hit it.

**Why the leak is invisible to vmstat on a stock kernel:** the msg is
allocated from the per-device malloc type `hmp->mmsg`
(kmalloc_create(&hmp->mmsg,"HAMMER2-msg"), hammer2_vfsops.c:1176) which
is kmalloc_destroy'ed at unmount (vfsops:1893) — the type vanishes from
`vmstat -m` while the allocation lives. (Separately observed, out of
scope: the global M_HAMMER2 "HAMMER2-mount" zone grows ~3.5KB/cycle even
on daemon-connected mounts — a different, unfixed accounting leak.)

**Fix validated:** fix.diff sets EXITNOACC and calls
kdmsg_drain_msgq(iocom) in uninit once the threads are gone (mirroring
the write-thread exit path, kern_dmsg.c:545-554; uninit holds msglk,
satisfying the drain's locking requirement). On the rebuilt kernel:
same trigger, 20/20 cycles succeed, `after ping write msgq.first=<ptr>`
(unchanged path) but `uninit exit msgq_nleak=0` and **0** DF2615_LEAK
lines. Mount/umount behavior unchanged.

## Files
- df2615_trigger.c   trigger: 20x mount(2){cluster_fd=-1}/unmount(2)
- run_df2615.sh      stock-kernel vmstat variant (kept for reference)
- instrument.diff    verification counters (NOT the fix)
- fix.diff           the fix (against pristine sys/)
- build.log          instrumented kernel build (nativekernel)
- build.attempt1_buildkernel_fail.log  why nativekernel is used
- build_baseline_kernel4.log, fix_build.log   baseline/fix kernels
- run_base.*         decisive baseline run (20 leak lines, full dmesg)
- run_fix.*          decisive fixed run (0 leak lines, full dmesg)
