β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3130

update_mp() re-executed on every remount with fspec re-opens all four iconv handles without closing the previous ones β€” unbounded kernel-heap handle leak via mount -u loop (partial-failure remounts leak the replaced handles too)

Summary

update_mp() opens pm_w2u/pm_u2w/pm_u2d/pm_d2u via msdosfs_iconv->open (:119-132) whenever MSDOSFSMNT_KICONV is set (sticky: pm_flags |= on every call, :113). msdosfs_mount reaches update_mp on the MNT_UPDATE path whenever args.fspec != NULL and matches pm_devvp (:258-279) - each 'mount -u -o <kiconv opts>' re-opens four handles, overwrites the pm_* pointers and leaks the old four (each a kernel iconv_converter allocation plus charset tables). N iterations = 4N leaked handles until unmount (which closes only the last four). Partial-failure remounts (:121-132 return without cleanup) leak the already-replaced handles as well. Privileged mount owner (root; or usermount owner) leaking ~4 converter handles per remount - kmem exhaustion over time; no memory corruption. Phase V skipped (Low). Fix: close existing handles before re-opening. [VERIFIED pass2: update_mp path live-reached 1187 prints but raw-syscall remount fails ENOENT in kernel libiconv on this guest; leak code-certain]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3130 Β· 10 files
FileTypeDescriptionSize
remount_leak.c β€” 2.1 KB view raw
run.log β€” 874 B view raw
README.md β€” 661 B ↓ raw
VERDICT.md β€” 2.3 KB ↓ raw
build.sh β€” 154 B view raw
run.sh β€” 181 B view raw
manifest.json β€” 748 B view raw
verdict.json β€” 2.0 KB view raw
fix.diff β€” 941 B view raw
env.txt β€” 272 B view raw

DF-3130 -- verification pack

Finding: update_mp re-executed on remount leaks four iconv handles per mount -u

Status: not_reproduced / impact leak / confidence certain

What was run

guest: cc -O -lkiconv -o remount_leak remount_leak.c

How to reproduce

guest: mount_msdos -o ro -L zh_CN.GB18030 -D GB18030 /dev/vn0 /mnt && ./remount_leak /dev/vn0 /mnt 40

Expected

code path reached (kernel prints local/dos per attempt); on this guest each re-open fails ENOENT in kernel libiconv so no leak is observable

See VERDICT.md for the full narrative and verdict.json for the persisted machine verdict. Artifacts: remount_leak.c, run.log.

VERDICT.md
↓ download raw

DF-3130 verification

Kernel: 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

Bottom line

The leak is code-certain (update_mp re-opens all four iconv handles on every MNT_UPDATE with fspec and overwrites pm_w2u/pm_u2w/pm_u2d/pm_d2u without closing, and pm_flags keeps MSDOSFSMNT_KICONV sticky so even KICONV-less updates re-open) and the path is confirmed live: 1187 kernel update_mp prints during the attempt loop. But on this guest every re-open of the previously-registered pairs fails with ENOENT inside kernel libiconv when invoked from a raw mount(2) MNT_UPDATE (the CLI pre-registers conversion tables via kiconv(3) at initial mount and no CLI path expresses a kiconv remount), so no handle churn occurs to observe and the vmstat -m iconv counters stay flat. Attempt harness and full logs retained; fix compile-validated.

Kernel references

Exploit chain

(none: availability/correctness finding; no privilege chain)

Fix

close existing handles before re-opening

See fix.diff in this directory (git-apply-able against the read-only sys/ tree; applied only inside the disposable verification guest).

Reproduction

build: guest: cc -O -lkiconv -o remount_leak remount_leak.c

run: guest: mount_msdos -o ro -L zh_CN.GB18030 -D GB18030 /dev/vn0 /mnt && ./remount_leak /dev/vn0 /mnt 40

expected: code path reached (kernel prints local/dos per attempt); on this guest each re-open fails ENOENT in kernel libiconv so no leak is observable

Notes on the PoC (what changed vs the finding sketch)

three iterations: struct msdosfs_args must come from (hand-rolled export_args ABI-mismatches), DFly mount(2) takes the fstype as first argument, and the kiconv(3) pair registration returns EINVAL for an already-registered pair so it was dropped (pm_flags is sticky)

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
↓ fix.diffper-fix-DF-3130

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (40+200 remount attempts, ENOENT, 1187 update_mp prints, iconv counters flat), remount_leak.c (uses the real <vfs/msdosfs/msdosfsmount.h> struct)

PoC changes

three iterations: struct msdosfs_args must come from (hand-rolled export_args ABI-mismatches), DFly mount(2) takes the fstype as first argument, and the kiconv(3) pair registration returns EINVAL for an already-registered pair so it was dropped (pm_flags is sticky)

Verified recommended fix

close existing handles before re-opening

Verdict

The leak is code-certain (update_mp re-opens all four iconv handles on every MNT_UPDATE with fspec and overwrites pm_w2u/pm_u2w/pm_u2d/pm_d2u without closing, and pm_flags keeps MSDOSFSMNT_KICONV sticky so even KICONV-less updates re-open) and the path is confirmed live: 1187 kernel update_mp prints during the attempt loop. But on this guest every re-open of the previously-registered pairs fails with ENOENT inside kernel libiconv when invoked from a raw mount(2) MNT_UPDATE (the CLI pre-registers conversion tables via kiconv(3) at initial mount and no CLI path expresses a kiconv remount), so no handle churn occurs to observe and the vmstat -m iconv counters stay flat. Attempt harness and full logs retained; fix compile-validated.