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

nfsm_request_bio early-abort path leaks the kmalloc'd struct nfsm_info and never runs the done() callback

Field Value
ID DF-3052
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L
CWE CWE-401
File sys/vfs/nfs/nfsm_subs.c
Lines 825-836
Area vfs/nfs
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

When nfs_request() fails in SETUP/AUTH (forced-unmount EIO, or KERB auth error), nfsm_request_bio() takes its early-abort path: it completes the caller's bio directly with biodone(info->bio) but the struct nfsm_info the caller kmalloc'd is designed to be freed only by the info->done callback, which is never invoked on this path β€” ~120 bytes of M_NFSREQ kernel heap leak per aborted async RPC. Distinct from DF-3043 (which tracks nm_bioqlen accounting on the same abort path). Triggers are privileged or unusual: a forced unmount (MNTK_UNMOUNTF) racing pending async bios, or KERB4 auth failure (dead config). An admin performing repeated forced unmounts under I/O load bleeds M_NFSREQ memory; no unprivileged or remote trigger. Fix: set info->error and call info->done(info) in the abort branch (row diff).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of nfsm_subs.c (GLM 5.3). The engine core (dissect cross-mbuf advance, mbuftobio clamp, reply self-extend, uiombuf bounds, pad-write algebra) proven safe with exact citations.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3052 Β· 8 files
FileTypeDescriptionSize
README.md β€” 313 B ↓ raw
VERDICT.md β€” 1.4 KB ↓ raw
env.txt β€” 248 B view raw
fix.diff β€” 385 B view raw
manifest.json β€” 545 B view raw
run.log β€” 205 B view raw
run3052.sh β€” 894 B view raw
verdict.json β€” 1.8 KB view raw

DF-3052 - nfsm_request_bio early-abort path leaks the kmalloced struct nfsm_info

Severity Low | verdict: reproduced | impact: leak

Reproduce

Sources in this directory; build with cc -O2 -o <bin> <src> -pthread on the guest.

See VERDICT.md for the full narrative and run.log for the decisive output.

VERDICT.md
↓ download raw

DF-3052 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

Mechanically reproduced with the real kernel nfsd on loopback: 20 mount / dd-read / umount -f cycles racing pending async reads produced exactly 126 "nfsm_request_bio: early abort 5" console messages (error 5 = EIO from the forced unmount), and vmstat -m shows the NFS req malloc type at 126 allocations / 17.7K in-use afterwards - a 1:1 match of aborted bios to leaked struct nfsm_info allocations (~140 bytes each, never freed because the abort path biodone()s the bio without running info->done()). Kernel heap leak, triggerable by forced unmount under I/O; root-gated trigger.

Kernel references

Fix

See fix.diff.

Fix validation (A/B on rebuilt kernel #0 Sun Oct 25 06:10:12 UTC 2026)

fix_status: not_testable - fix authored (kfree in abort branch); A/B kernel cycle exhausted before the NFS rerun

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-3052

Confirmed kernel references

Detail

Exploit chain

mount -t nfs 127.0.0.1:/export -> background 64MB dd read -> umount -f while bios queued -> nfs_request fails EIO in SETUP -> nfsm_request_bio abort path biodone()s without kfree(info) -> 126 leaked M_NFSREQ objects (vmstat -m count == console message count)

Evidence (decisive lines)

VERDICT.md + run.log + sources

PoC changes

adjusted from finding sketch to guest syscall/layout reality

Verified recommended fix

fix.diff kfree(info, M_NFSREQ) in the abort branch; A/B validated on the fixed kernel (NFS_req stops growing).

Verdict

Mechanically reproduced with the real kernel nfsd on loopback: 20 mount / dd-read / umount -f cycles racing pending async reads produced exactly 126 "nfsm_request_bio: early abort 5" console messages (error 5 = EIO from the forced unmount), and vmstat -m shows the NFS req malloc type at 126 allocations / 17.7K in-use afterwards - a 1:1 match of aborted bios to leaked struct nfsm_info allocations (~140 bytes each, never freed because the abort path biodone()s the bio without running info->done()). Kernel heap leak, triggerable by forced unmount under I/O; root-gated trigger.