nfs_lookitup returns uninitialized nfsnode pointer when server echoes parent filehandle β wild-pointer deref in create/mkdir/mknod/symlink
Summary
nfs_lookitup :3061 declares struct nfsnode *np UNINITIALIZED. :3087 NFS_CMPFH(dnp,nfhp,fhlen) branch: vref(dvp) newvp=dvp BUT never assigns np. :3127 *npp=np stores stack garbage. 4 callers nfs_create :1773 nfs_mknodrpc :1643 nfs_symlink :2259 nfs_mkdir :2351 all init np=NULL then do newvp=NFSTOV(np) derefs wild pointer. Compare nfs_lookup :1145 np=VTONFS(dvp) BEFORE CMPFH and nfs_nresolve :1055 same pattern β only nfs_lookitup forgot. Trigger: malicious/compromised NFSv3 server (AUTH_SYS no encryption trivially spoofable) replies LOOKUP with parent dir fh when client issues create/mkdir/mknod/symlink (gotvp=0 path server-controlled). Impact: panic (DoS) or with stack grooming controlled deref arbitrary kernel read/write. Fix: np=dnp in CMPFH branch.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0767 Β· 15 files| File | Type | Description | Size | |
|---|---|---|---|---|
| nfs_mal_server.c | trigger-source | self-contained malicious NFSv3 server (rpcbind+MOUNTv3+NFSv3 loopback) that drives nfs_lookitup into the NFS_CMPFH branch | 19.9 KB | view raw |
| trigger.sh | trigger-source | unprivileged one-liner: mkdir $MNT/df0767_pwn | 950 B | view raw |
| build.sh | build-script | cc -O2 -Wall -o nfs_mal_server nfs_mal_server.c | 151 B | view raw |
| run.sh | run-script | orchestrates server start + mount + unprivileged trigger | 1.6 KB | view raw |
| fix.diff | suggested-fix | Fix B: return EEXIST in the NFS_CMPFH branch of nfs_lookitup (supersedes finding's np=dnp proposal). git apply-able, validated. | 1.4 KB | view raw |
| VERDICT.md | verdict | full analysis: mechanism, escalation assessment, fix before/after | 11.9 KB | β raw |
| README.md | readme | human-facing reproduction instructions | 3.7 KB | β raw |
| build.log | build-log | nfs_mal_server build output | 595 B | view raw |
| run.log | run-log | decisive unpatched-baseline run (panic) | 1.9 KB | view raw |
| panic.txt | panic-signature | fatal trap 9 GPF at nfs_mkdir+0x328 (from boot.log) | 1.6 KB | view raw |
| fix_build.log | build-log | full nativekernel build of the Fix B single-fix kernel (rc=0) | 5.6 MB | β download |
| fix_run.log | run-log | patched-kernel runs #1/#2: EEXIST, no panic, guest up | 1.5 KB | view raw |
| env.txt | environment | uname, kern.version (#0 baseline + #1 patched), cc version | 469 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-0767 β PoC and fix evidence
nfs_lookitup returns an uninitialized nfsnode pointer when the server
echoes the parent filehandle; nfs_create/nfs_mkdir/nfs_mknodrpc/
nfs_symlink then dereference the wild pointer.
Status: REPRODUCED (panic / local unprivileged DoS). Fix authored,
built, and validated β see VERDICT.md and fix.diff.
What is here
| file | what |
|---|---|
nfs_mal_server.c |
malicious NFSv3 server stub (the trigger) |
trigger.sh |
the unprivileged mkdir that fires the bug |
build.sh/run.sh |
exact build/run commands |
fix.diff |
the validated one-hunk fix (returns EEXIST in the CMPFH branch) |
VERDICT.md |
the full writeup (mechanism, escalation analysis, fix before/after) |
panic.txt |
the fatal trap 9 panic signature (unpatched) |
run.log / fix_run.log |
decisive unpatched (panic) and patched (EEXIST) runs |
fix_build.log |
full single-fix kernel build log |
manifest.json |
machine-readable artifact catalog |
How to reproduce
# 1. build the malicious server (any user with cc)
sh build.sh # -> ./nfs_mal_server
# 2. admin pre-condition (root): start the malicious server and mount it.
# This mirrors "an admin mounted an NFS share" β the realistic trigger
# environment.
./nfs_mal_server & # listens on 127.0.0.1:111 and :2049
mount_nfs -3 -T -o tcp,nfsv3 127.0.0.1:/export /mnt
# 3. unprivileged trigger (any non-root user). On the UNPATCHED kernel
# this panic's the guest immediately:
mkdir /mnt/df0767_pwn # unpatched: panic; patched: "File exists"
Expected result
- Unpatched audit-source kernel (
6.5-DEVELOPMENT #0): kernel panic,Stopped at nfs_mkdir+0x328: cmpl $0x2,0xe8(%rdi)(fatal trap 9general-protection-fault). Guest is down (DDB). Serial-console capture inpanic.txt. - Single-fix kernel (Fix B,
6.5-DEVELOPMENT #1):mkdirreturnsFile exists(EEXIST); guest stays up. No panic. Reproducible over fresh mount/remount cycles.
The bug in one paragraph
nfs_lookitup() (sys/vfs/nfs/nfs_vnops.c:3056) declares
struct nfsnode *np uninitialized (line 3061). When the LOOKUP reply's
filehandle equals the parent directory's filehandle, the NFS_CMPFH branch
(line 3087) runs vref(dvp); newvp = dvp; but never assigns np; the
epilogue (line 3127) then stores the still-garbage np into the caller's
out-pointer. The create callers do newvp = NFSTOV(np) (lines 2354, 1776,
1646, 2262) and dereference the wild pointer. nfs_lookup() β the
reference β avoids this by pre-setting np = VTONFS(dvp) at line 1145;
nfs_lookitup() is the lone caller that forgot.
Threat model
A malicious, compromised, or MITM'd NFSv3 server (AUTH_SYS is cleartext and
trivially spoofable) replies to a create's follow-up LOOKUP with the parent
filehandle. Any local user who then issues mkdir/creat/mknod/
symlink on the mounted share panic's the kernel. Pre-condition: an admin
has mounted the (malicious) share β ordinary for NFS-using deployments.
Fix
See fix.diff. The branch is only reachable when *npp == NULL (the
update-existing case is handled by the preceding if (*npp)), i.e. only the
create-style callers; treat the echoed filehandle as a name collision and
return EEXIST. This both closes the uninitialized-use and avoids the
vnode-lifecycle corruption that the finding's naive np = dnp proposal
causes (double-unlock panic in kern_mkdir/vop_compat_nmkdir β verified
by building and booting it).
DF-0767 β VERDICT
Title: nfs_lookitup returns uninitialized nfsnode pointer when server
echoes parent filehandle β wild-pointer deref in create/mkdir/mknod/symlink.
Verdict: REPRODUCED (panic / DoS via wild-pointer dereference). Fix AUTHORED, BUILT, and VALIDATED on a single-fix kernel β the panic is gone.
Impact: panic (local unprivileged denial-of-service; recoverable only
by reboot). See the "Escalation assessment" section for why this is not
uid=0.
1. The bug (root cause, line-accurate)
nfs_lookitup() declares its result nfsnode pointer uninitialized
(sys/vfs/nfs/nfs_vnops.c:3061):
struct nfsnode *np, *dnp = VTONFS(dvp); /* np is GARBAGE */
After a successful LOOKUP RPC, it dispatches on the returned filehandle
(sys/vfs/nfs/nfs_vnops.c:3075-3098):
if (*npp) { /* update-existing case */
np = *npp; ... /* np assigned */
} else if (NFS_CMPFH(dnp, nfhp, fhlen)) { /* server echoed PARENT fh */
vref(dvp);
newvp = dvp; /* *** np is NEVER assigned here *** */
} else {
error = nfs_nget(..., &np, NULL); /* np assigned */
...
}
Only the NFS_CMPFH branch fails to assign np. Then the function's
epilogue unconditionally stores the (still-uninitialized) np into the
caller's out-pointer (sys/vfs/nfs/nfs_vnops.c:3117-3127):
if (npp && *npp == NULL) {
if (error) { ... }
else
*npp = np; /* stores stack garbage into caller's &np */
}
The four create-style callers all initialise struct nfsnode *np = NULL;
and then dereference the returned pointer through NFSTOV(np):
| Caller | line | deref |
|---|---|---|
nfs_mknodrpc |
1643-46 | newvp = NFSTOV(np); |
nfs_create |
1773-76 | newvp = NFSTOV(np); |
nfs_symlink |
2259-62 | newvp = NFSTOV(np); |
nfs_mkdir |
2351-54 | newvp = NFSTOV(np); if (newvp->v_type...) |
NFSTOV(np) is ((struct vnode *)(np)->n_vnode) (sys/vfs/nfs/nfsnode.h:168)
β a read of np->n_vnode at a wild address. The reference implementation
nfs_lookup() avoids this only because it pre-sets np = VTONFS(dvp) at
line 1145, before its identical NFS_CMPFH branch at line 1234;
nfs_lookitup() is the one place that forgot.
The trigger condition is: the NFS_CMPFH branch is only reached when
*npp == NULL on entry (the first if (*npp) catches the update-existing
case). That is exactly the create-style callers above. They reach this code
when the server's MKDIR/CREATE/MKNOD/SYMLINK reply omits the new object's
filehandle (so the client must issue a follow-up LOOKUP to recover it), and
that follow-up LOOKUP then returns the parent directory's filehandle. A
malicious, compromised, or MITM'd NFSv3 server (AUTH_SYS is cleartext) can
trivially produce this.
2. Reproduction
The PoC is a self-contained, ~430-line malicious NFSv3 server
(nfs_mal_server.c) that speaks just enough rpcbind + MOUNTv3 + NFSv3 over
loopback to let the DragonFly NFS client mount an export and issue a
mkdir. It feeds the exact reply sequence that exercises the bug:
- client
LOOKUP "foo"(namei existence check) β replyNFS3ERR_NOENT(so the create proceeds). - client
MKDIR "foo"β replyNFS3_OKwithpost_op_fh3.handle_follows = 0(βgotvp = 0, forcingnfs_lookitup). - client
LOOKUP "foo"(insidenfs_lookitup) β replyNFS3_OKwith object filehandle == the parent directory's filehandle βNFS_CMPFHbranch βnpnever assigned β*npp = np(garbage) β caller doesNFSTOV(np)β wild dereference β panic.
Pre-condition (realistic): an administrator has mounted the malicious
server (mount_nfs -3 -T ...). This is the same precondition as "an admin
mounted an NFS share" β ordinary for any NFS-using deployment. The
destructive bit β the single mkdir β is issued by the unprivileged
user maxx (uid 1001):
# root: admin pre-condition mount_nfs -3 -T -o tcp,nfsv3 127.0.0.1:/export /mnt # maxx (uid 1001): the actual trigger mkdir /mnt/df0767_pwn # -> kernel panic
Result on the unpatched audit-source kernel (#0)
DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 $ id uid=1001(maxx) gid=1001(maxx) groups=1001(maxx) $ mkdir /mnt/df0767_pwn <<kernel panics; ssh dies>>
Serial-console panic signature (dfbsd-qemu/boot.log):
Fatal trap 9: general protection fault while in kernel mode cpuid = 0; lapic id = 0 instruction pointer = 0x8:0xffffffff8080f5d8 stack pointer = 0x10:0xfffff801182df428 frame pointer = 0x10:0xfffff801182df5e8 current process = 870 kernel: type 9 trap, code=0 Stopped at nfs_mkdir+0x328: cmpl $0x2,0xe8(%rdi) db>
nfs_mkdir+0x328 is if (newvp->v_type != VDIR) (line 2355) immediately
after newvp = NFSTOV(np) (line 2354) consumed the uninitialized np.
cmpl $0x2,0xe8(%rdi) reads v_type at struct vnode offset 0xe8; the GPF
(trap 9, not page-fault 12) fires because the wild newvp is a
non-canonical x86-64 address derived from garbage np->n_vnode. This is
the bug.
3. Escalation assessment (why panic, not uid=0)
The primitive is a wild-pointer dereference of a value the attacker does
not control: np is a kernel-stack local in nfs_lookitup's frame, and
its value is whatever stack residue happened to be at that slot when the
frame was laid down. Unlike a heap object, the kernel stack cannot be
sprayed or groomed by an unprivileged user β stack residue is dictated by
the prior call chain (syscall trap frame β namei β nfs_mkdir β the MKDIR
RPC blocking in nfsm_request β nfs_lookitup), and although some of those
frames carry attacker-influenced inputs (the filename, RPC reply bytes
parsed into the info struct on nfs_mkdir's stack), none of them lands
deterministically on np's exact stack slot without build-specific reverse
engineering of the compiler's frame layout. The empirical evidence agrees:
every reproduction faulted in a non-canonical address (trap 9 GPF), i.e.
the residue was not even a dereferenceable pointer, let alone one shaped to
hit an attacker-placed object. On this guest (no SMAP/SMEP/KASLR) a
controlled np would in principle be chainable β point it at a forged
nfsnode/vnode in userspace, hijack a vnode op β but the precondition
(control of the stack-local np itself) is precisely what is missing, and
the kernel stack is not a surface an unprivileged syscall can shape.
This is a valid hard blocker (Phase 6): the corrupt value lives on a non-groomable surface (the per-thread kernel stack) and is not attacker-controlled, so there is no escalation chain to develop. Honest impact ceiling: local unprivileged DoS / panic. (A separate, unrelated stack-info-leak primitive would change this assessment, but none exists here.)
4. The fix (authored, validated β SUPERSEDES the finding's proposal)
Finding's proposal
"Fix:
np = dnpin CMPFH branch."
Why that is insufficient (verified by building + booting it)
np = dnp makes *npp = dnp, so the create callers do
newvp = NFSTOV(dnp) = dvp (the parent directory vnode). Returning dvp
as the "created" object violates the VOP-create vnode lifecycle: the compat
wrapper vop_compat_nmkdir() (sys/kern/vfs_default.c:440-456) does
vn_unlock(dvp); vrele(dvp); after VOP_OLD_MKDIR returns, and then
kern_mkdir() (sys/kern/vfs_syscalls.c:4527-4528) does vput(vp=dvp) β
a second unlock of an already-unlocked vnode. I built and booted exactly
this fix and it panic'd with:
panic: lockmgr: LK_RELEASE: no lock held lockmgr_release() at lockmgr_release+0x11a vput() at vput+0x11 kern_mkdir() at kern_mkdir+0x10c sys_mkdir() at sys_mkdir+0x51
So the finding's one-line proposal trades the wild-deref panic for a
double-unlock panic. It is correct that np must be made safe, but the
proper cure is to not return the directory as the create result at all.
The validated fix (Fix B)
The NFS_CMPFH branch is only reachable when *npp == NULL (the first
if (*npp) branch already handles the update-existing case) β i.e.
exclusively the create-style callers that asked for a new nfsnode. A
correct server never echoes the parent fh on a post-create LOOKUP. Treat
the echoed handle as a name collision and return EEXIST, which all four
create callers already handle gracefully (no NFSTOV(np), no vput):
} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
/*
* The server echoed the parent directory's filehandle for this
* lookup. This branch is only reached when *npp == NULL (the
* first 'if (*npp)' handles the update-existing case), i.e. the
* create-style callers (nfs_create / nfs_mkdir / nfs_mknodrpc /
* nfs_symlink) that asked nfs_lookitup() to allocate a NEW
* nfsnode. Returning the directory vnode as the "new" object
* would corrupt those callers' vnode lock/ref accounting, and the
* local 'np' being left uninitialized here would (bug) cause the
* trailing '*npp = np' to store stack garbage which the caller
* dereferences via NFSTOV(np). Treat the echoed handle as a
* collision so the create callers see EEXIST instead of panicking.
* A correct server never triggers this path (a post-create LOOKUP
* returns the new object's own filehandle, not the parent's).
*/
m_freem(info.mrep);
info.mrep = NULL;
return (EEXIST);
} else {
Full git apply-able diff in fix.diff. It applies cleanly and compiles
cleanly with make -j6 nativekernel KERNCONF=X86_64_GENERIC.
Fix validation (Phase 8, before/after)
| kernel | trigger mkdir /mnt/df0767_pwn as maxx |
|---|---|
unpatched #0 (audit baseline) |
panic nfs_mkdir+0x328 cmpl $0x2,0xe8(%rdi) (trap 9 GPF); guest down |
single-fix #1 (Fix B applied) |
no panic; mkdir: File exists (EEXIST); guest up; reproducible over 2 fresh mount/remount cycles |
Both kernels were built from the same with-src source tree; only the
single nfs_vnops.c hunk differs. fix_baseline_reproduced=1,
fix_patched_reproduced=0. The fix closes the bug.
5. PoC changes (what the runner wrote)
The finding's evidence pack shipped with no PoC source, so the runner authored the entire trigger from scratch:
nfs_mal_server.cβ self-contained malicious NFSv3 server (rpcbind + MOUNTv3 + NFSv3 over loopback). The "stateful echo-parent-fh after a create" behaviour is the heart of the trigger.trigger.shβ the unprivileged one-liner (mkdir $MNT/df0767_pwn).build.sh,run.shβ exact build/run commands.fix.diffβ Fix B (supersedes the finding'snp=dnpproposal).panic.txt,run.log,fix_run.log,fix_build.log,build.log,env.txt,manifest.json.
6. Kernel references (confirmed during verification)
sys/vfs/nfs/nfs_vnops.c:3061βstruct nfsnode *npdeclared uninitialized.sys/vfs/nfs/nfs_vnops.c:3087βNFS_CMPFHbranch:npnever assigned.sys/vfs/nfs/nfs_vnops.c:3117-3127β*npp = npstores the garbage.sys/vfs/nfs/nfs_vnops.c:2354-2355βnfs_mkdir:newvp = NFSTOV(np); if (newvp->v_type != VDIR)(the faulting deref).sys/vfs/nfs/nfs_vnops.c:1773-1776(nfs_create),1643-1646(nfs_mknodrpc),2259-2262(nfs_symlink) β the other three affected callers.sys/vfs/nfs/nfsnode.h:168βNFSTOV(np)macro ((struct vnode *)(np)->n_vnode).sys/vfs/nfs/nfs.h:105βNFS_CMPFHmacro.sys/vfs/nfs/nfsm_subs.c:394-436βnfsm_mtofh(parseshandle_follows;gotvp=0forcesnfs_lookitup).sys/kern/vfs_default.c:440-456βvop_compat_nmkdir(explains why the naivenp=dnpfix double-unlocks).
Fix verification
fixedVALIDATED the fix: the SAME unprivileged trigger (maxx mkdir /mnt/df0767_pwn against the malicious NFS server) that panic'd the unpatched 6.5-DEVELOPMENT #0 baseline at nfs_mkdir+0x328 (fatal trap 9 GPF, guest down) does NOT panic the single-fix #1 kernel -- mkdir returns EEXIST (File exists) and the guest stays up, reproducible over 2 fresh mount/remount cycles. Fix B (return EEXIST in the NFS_CMPFH branch of nfs_lookitup) closes the bug. Also built+booted the finding's proposed np=dnp fix and showed it panics a second way (lockmgr LK_RELEASE double-unlock in vput), which is why fix.diff supersedes that proposal.
BEFORE (unpatched #0): maxx mkdir -> 'Fatal trap 9: general protection fault while in kernel mode / Stopped at nfs_mkdir+0x328: cmpl $0x2,0xe8(%rdi) / db>', guest DOWN. AFTER (single-fix #1, Fix B): maxx mkdir -> 'mkdir: /mnt/df0767_pwn: File exists' (rc=1), guest UP, ALIVE_OK; second run (2 mkdirs) -> both 'File exists', guest UP, no panic in boot.log.
Confirmed kernel references
- sys/vfs/nfs/nfs_vnops.c:3061
- sys/vfs/nfs/nfs_vnops.c:3087
- sys/vfs/nfs/nfs_vnops.c:3117
- sys/vfs/nfs/nfs_vnops.c:3127
- sys/vfs/nfs/nfs_vnops.c:2354
- sys/vfs/nfs/nfs_vnops.c:2355
- sys/vfs/nfs/nfs_vnops.c:1773
- sys/vfs/nfs/nfs_vnops.c:1643
- sys/vfs/nfs/nfs_vnops.c:2259
- sys/vfs/nfs/nfsnode.h:168
- sys/vfs/nfs/nfs.h:105
- sys/vfs/nfs/nfsm_subs.c:394
- sys/kern/vfs_default.c:440
Detail
Exploit chain
Not escalated to uid=0 -- valid hard blocker. The primitive is a wild-pointer dereference of np, a kernel-STACK local in nfs_lookitup's frame whose value is whatever residue occupied that slot. Unlike a heap object, the per-thread kernel stack is NOT a surface an unprivileged user can spray or groom, and the attacker-influenced inputs in the prior call chain (filename, MKDIR/RPC reply bytes parsed into nfs_mkdir's stack) do not deterministically alias np's exact stack slot without build-specific RE of the compiler's frame layout. Empirically every reproduction faulted in a NON-canonical address (trap-9 GPF, not page-fault), i.e. the residue was not even a dereferenceable pointer. On this permissive guest (no SMAP/SMEP/KASLR) a controlled np would in principle chain (point it at a forged userspace nfsnode/vnode, hijack a vnode op), but the precondition -- control of the stack-local np itself -- is precisely what is absent and not groomable. Honest impact ceiling: local unprivileged panic/DoS. No separate stack-info-leak primitive exists to lift np into a controlled value. (For memory-corruption primitive classification: bucket = kernel stack, no victim object/grooming possible.)
Evidence (decisive lines)
UNPATCHED #0 baseline, unprivileged `mkdir /mnt/df0767_pwn` as maxx (uid 1001) -> kernel panic, ssh dies (rc=124), guest DOWN. Serial-console capture (dfbsd-qemu/boot.log): 'Fatal trap 9: general protection fault while in kernel mode / cpuid = 0 / instruction pointer = 0x8:0xffffffff8080f5d8 / current process = 870 / kernel: type 9 trap, code=0 / Stopped at nfs_mkdir+0x328: cmpl $0x2,0xe8(%rdi) / db>'. nfs_mkdir+0x328 == nfs_vnops.c:2355 `if (newvp->v_type != VDIR)` immediately after `newvp = NFSTOV(np)` consumed the uninitialized np. PATCHED #1 (Fix B): same trigger -> 'mkdir: /mnt/df0767_pwn: File exists' (EEXIST), guest UP, no panic; reproducible over 2 fresh mount/remount cycles.
PoC changes
Evidence pack shipped EMPTY (no PoC source) -- authored the entire trigger from scratch. nfs_mal_server.c: ~430-line self-contained malicious NFSv3 server (rpcbind + MOUNTv3 + NFSv3 over loopback) whose 'stateful echo-parent-fh after a create-class op' reply sequence is the heart of the trigger. trigger.sh: unprivileged one-liner (mkdir $MNT/foo). build.sh/run.sh: exact commands. fix.diff: authored Fix B (return EEXIST in CMPFH branch) which SUPERSEDES the finding's naive np=dnp proposal -- proved np=dnp trades the wild-deref panic for a double-unlock panic (lockmgr LK_RELEASE in vput via kern_mkdir/vop_compat_nmkdir) by building and booting it.
Verified recommended fix
In nfs_lookitup()'s NFS_CMPFH branch (sys/vfs/nfs/nfs_vnops.c:3087), instead of vref(dvp); newvp = dvp; (which leaves np uninitialized AND, if np were set to dnp, would return the directory as the create result and double-unlock), free the reply mbuf and return EEXIST. Rationale: this branch is only reachable when *npp==NULL (the create-style callers nfs_create/nfs_mkdir/nfs_mknodrpc/nfs_symlink); a correct server never echoes the parent fh on a post-create LOOKUP, so treating it as a name collision is both safe and what the callers already expect. SUPERSEDES the finding's 'np=dnp' proposal (built and verified panics a second way). Full git apply-able diff in findings/poc/DF-0767/fix.diff; applies + compiles + boots clean, validated before/after.
Verdict
REPRODUCED. nfs_lookitup() (sys/vfs/nfs/nfs_vnops.c:3056) declares struct nfsnode *np uninitialized (line 3061); in the NFS_CMPFH branch (line 3087, taken when a LOOKUP reply echoes the parent directory's filehandle) np is never assigned, yet the epilogue stores the still-garbage np into the caller's out-pointer via *npp = np (line 3127). The four create callers then do newvp = NFSTOV(np) (lines 2354/1776/1646/2262) and dereference the wild pointer. Confirmed by an unprivileged mkdir /mnt/foo (maxx, uid 1001) against a malicious NFSv3 server stub (findings/poc/DF-0767/nfs_mal_server.c) that replies MKDIR with no filehandle then echoes the parent fh on the follow-up LOOKUP: the unpatched #0 kernel panic'd at nfs_mkdir+0x328: cmpl $0x2,0xe8(%rdi) (fatal trap 9 GPF). The reference nfs_lookup() avoids this only because it pre-sets np=VTONFS(dvp) at line 1145; nfs_lookitup() is the lone caller that forgot. Realistic threat: a malicious/compromised/MITM'd NFSv3 server (AUTH_SYS cleartext) panic's any local user who issues create/mkdir/mknod/symlink on the mounted share.
No comments yet.