Namecache-reference leak on failed jail(2): pr_root handle never dropped on kern_jail error paths (deterministic via jail path = regular file)
| Field | Value |
|---|---|
| ID | DF-2801 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L |
| CWE | CWE-401 Missing Release of Memory |
| File | sys/kern/kern_jail.c |
| Lines | 206 (cache_copy), 211-244 (error paths), 342-351 (sys_jail out) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-31 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
kern_jail() cache_copy()s the jail path's namecache handle into pr->pr_root; every error path after that (assign_prison_id ERANGE, prison_sysctl_create failure, kern_jail_attach failure) cleans varsyms/nlookup but never drops it, and sys_jail kfrees pr with the reference live. Deterministic trigger: jail path = a regular file (nlookup never requires a directory; kern_chroot then fails ENOTDIR). Measured with pressure/settle floors: 2000 failed calls permanently pin ~1538 namecache entries (control β104). Root-gated privileged exhaustion primitive; patched kernel +118 vs β91 (noise).
Recommended fix
NULL-guarded cache_drop on every error path and in sys_jail's out (cache_drop is not NULL-safe) β findings/poc/DF-2801/fix.diff.
Timeline
- 2026-08-31 Discovered during pass-2 audit of kern_jail.c (GLM 5.3); leak quantified + fix validated same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2801 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| jailleak.c | β | 3.3 KB | view raw | |
| run.sh | β | 1.7 KB | view raw | |
| run.log | β | 474 B | view raw | |
| run.2.log | β | 509 B | view raw | |
| run.3.log | β | 553 B | view raw | |
| run.patched.log | β | 352 B | view raw | |
| env.txt | β | 373 B | view raw | |
| fix.diff | β | 5.1 KB | view raw | |
| verdict.json | β | 3.6 KB | view raw |
Fix verification
fixedPatched kernel: identical trigger (2000 ENOTDIR failures) pins nothing β leak-cycle floor growth +118 vs control -91 (noise), against +1538 on stock. Permanent pinning eliminated.
findings/poc/DF-2801/run.patched.log; fix.diff (kern_jail.c cache_drop hunks)
Confirmed kernel references
Detail
Exploit chain
host root: loop { jail(path=
Evidence (decisive lines)
['findings/poc/DF-2801/run.3.log β decisive stock run: F0 6476 / control -104 / leak +1538, ENOTDIR=2000, REPRODUCED line', 'findings/poc/DF-2801/run.patched.log β fixed: control -91, leak +118 (noise), ENOTDIR=2000 same path']
PoC changes
two measurement fixes: (1) vfs.cache.numcache is an INT sysctl β first version parsed it as a string and read 0; (2) baseline must be compared as settled floors with reclaim pressure between phases β instantaneous deltas are confounded by lazy hammer2 reclaim (first design took the baseline after file creation and showed delta 0).
Verified recommended fix
Add NULL-guarded cache_drop(&pr->pr_root) to every kern_jail error return after cache_copy and to sys_jail's out path before kfree(pr).
Verdict
REPRODUCED: failed jail(2) calls permanently pin namecache entries. kern_jail() cache_copy()s the jail path handle into pr->pr_root (kern_jail.c:206) and no error path after that drops it (assign_prison_id :211-216, prison_sysctl_create, kern_jail_attach/out2 :227-244; sys_jail kfrees pr at :342-351 with the reference live). Trigger: jail path = regular file β nlookup succeeds, cache_copy runs, kern_chroot/checkvp_chdir fails ENOTDIR (vfs_syscalls.c:2058). Measured with a floor-comparison design (2000 files per cycle, 8000-file pressure churn + settle between phases to defeat lazy hammer2 reclaim): stock kernel control cycle -104 vs leak cycle +1538 permanently-pinned entries for 2000 calls (all 2000 failing ENOTDIR, i.e. exactly the traced path). Patched kernel (cache_drop on every error path, NULL-guarded): leak growth collapses to +118, same order as control noise, with the identical ENOTDIR failure count. Root-gated (jail(2) needs SYSCAP_NOJAIL_CREATE) so this is a privileged-user unbounded-kernel-memory-exhaustion primitive, not an unpriv escalation β rated Low severity, impact ceiling DoS.
No comments yet.