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)
PoC verification
Evidence pack
findings/poc/DF-3052 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.
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_testableConfirmed 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.
No comments yet.