nlookup_init()/retry path read the shared fd_ncdir/fd_nrdir nchandle pairs without fd_spin while sibling threads swap them β torn (ncp,mount) pairs and hold-after-drop race on the jail '..'-gate comparison inputs
| Field | Value |
|---|---|
| ID | DF-2717 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:L |
| CWE | CWE-362 Improper Synchronization |
| File | sys/kern/vfs_nlookup.c |
| Lines | 149-161, 225-242, 1380 (torn read vfs_cache.c:996-1025) |
| Area | kern |
| Confidence | likely |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | memcorrupt |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
Writers serialize fd_ncdir/fd_nrdir under fdp->fd_spin (kern_fchdir, kern_chdir) β and kern_chroot updates fd_nrdir under p_token only, so the writers are not even mutually consistent. The nlookup reader side takes no lock at all: nlookup_init caches nl_basench and immediately cache_copy()s it (a non-atomic two-word load then _cache_hold on the loaded ncp), nlookup_init_at likewise, and every generation-mismatch retry re-dereferences nl_basench after an arbitrarily long walk. Under x86 TSO a chdir/fchdir storm from a sibling thread (fully unprivileged) can interleave to give the reader (old ncp, new mount) β a mismatched handle whose mount half flows into the root/jail '..'-gate equality check (confinement is solely mount+ncp equality) β or, in the tighter corner where the old cwd ncp is simultaneously being torn out of the namecache topology, a _cache_hold on an ncp whose last ref was just dropped β a use-after-free-grade refcount corruption. An established reader protocol exists (checkdirs takes fd_spin shared); nlookup is the hot path that omits it.
Threat model & preconditions
Unprivileged multi-threaded local process: thread A in path lookup while thread B hammers chdir/fchdir. Realistic impact: correctness anomalies/DoS-class misbehavior with a speculative jail-gate-bypass ceiling. Not guest-verified (two-adjacent-loads window; no deterministic oracle without instrumentation) β stands on the static protocol violation.
Recommended fix
Snapshot the pairs under the existing lock: take spin_lock_shared(&p->p_fd->fd_spin) around the cache_copy in nlookup_init/nlookup_init_at, make kern_chroot update fd_nrdir under fd_spin like chdir/fchdir already do, and stop re-reading nl_basench on retry (use the init-time snapshot or re-read under the spin).
Timeline
- 2026-08-30 Discovered during pass-2 audit of vfs_nlookup.c (GLM 5.3).
No comments yet.