netaddr_match union type confusion for RC_NAM cache entries: rc_nam pointer compared as an IPv4 address in the dup-reply-cache hash walk β spoofed-source false cache hits and a silently inert duplicate cache for non-AF_INET transports
Summary
Both hash-walk matches call netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam) unconditionally (:194,:307), regardless of whether the entry stored an IPv4 address (RC_INETADDR) or a dup'd sockaddr pointer (RC_NAM - the default branch for any non-AF_INET client address, e.g. an AF_INET6 nfsd datagram socket, which nfssvc_addsock accepts without a family check). netaddr_match reads haddr->had_inetaddr for family AF_INET, i.e. the low 32 bits of the rc_nam heap pointer, and compares it to the requester's sin_addr. Consequences: (a) an IPv4 client that spoofs a source address equal to the low 32 bits of a live rc_nam allocation (guessable 32-bit equality oracle observable via hit-vs-miss reply behavior) gets a false cache hit on another (v6) client's entry - dropping its request or resending that client's cached reply (file handles, attributes) to the spoofer; (b) for a v6 transport the match can never succeed, so the duplicate-reply cache is silently inert - every retransmission of a non-idempotent operation re-executes, the exact hazard the cache exists to prevent. Requires admin to run legacy kernel nfsd on a non-AF_INET socket (unusual config, root-only setup). No memory corruption. Not PoC'd (config-gated Low, skipped per contract). Fix: pass the entry's actual family / branch on RC_NAM. [VERIFIED pass2: needs nfsd on v6 socket plus spoofed probes; AF_INET type confusion line-certain; fix compile-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3214 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | β | 1.0 KB | view raw | |
| README.md | β | 400 B | β raw | |
| VERDICT.md | β | 1.7 KB | β raw | |
| build.sh | β | 106 B | view raw | |
| run.sh | β | 131 B | view raw | |
| manifest.json | β | 512 B | view raw | |
| verdict.json | β | 1.4 KB | view raw | |
| fix.diff | β | 1.0 KB | view raw | |
| env.txt | β | 272 B | view raw |
DF-3214 -- verification pack
Finding: netaddr_match AF_INET type confusion for RC_NAM cache entries
Status: not_reproduced / impact leak / confidence likely
What was run
n/a
How to reproduce
n/a (needs nfsd on a v6 socket + spoofed v4 probes)
Expected
n/a
See VERDICT.md for the full narrative and verdict.json for the persisted machine verdict. Artifacts: fix.diff.
DF-3214 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
Bottom line
Type confusion certain by inspection: both hash-walk matches call netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam) unconditionally regardless of whether the entry stored an IPv4 address (RC_INETADDR) or a duped sockaddr (RC_NAM, the default for non-AF_INET transports), so a v6-created entry has its rc_nam heap pointer read as an IPv4 address - 32-bit equality oracle for a spoofer and a silently inert duplicate cache for v6. Dynamic proof requires running legacy kernel nfsd on an AF_INET6 datagram socket plus source-spoofed probes; the config gate (admin sets up v6 nfsd) kept it out of this batch. Fix branches on RC_INETADDR; compile-validated.
Kernel references
Exploit chain
(none: availability/correctness finding; no privilege chain)
Fix
branch the cache-walk match on RC_INETADDR
See fix.diff in this directory (git-apply-able against the read-only sys/ tree; applied only inside the disposable verification guest).
Reproduction
build: n/a
run: n/a (needs nfsd on a v6 socket + spoofed v4 probes)
expected: n/a
Notes on the PoC (what changed vs the finding sketch)
(none)
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
Evidence (decisive lines)
VERDICT.md walk over nfs_srvcache.c:194/:307 with the RC_INETADDR/RC_NAM storage at :258-275
Verified recommended fix
branch the cache-walk match on RC_INETADDR
Verdict
Type confusion certain by inspection: both hash-walk matches call netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam) unconditionally regardless of whether the entry stored an IPv4 address (RC_INETADDR) or a duped sockaddr (RC_NAM, the default for non-AF_INET transports), so a v6-created entry has its rc_nam heap pointer read as an IPv4 address - 32-bit equality oracle for a spoofer and a silently inert duplicate cache for v6. Dynamic proof requires running legacy kernel nfsd on an AF_INET6 datagram socket plus source-spoofed probes; the config gate (admin sets up v6 nfsd) kept it out of this batch. Fix branches on RC_INETADDR; compile-validated.
No comments yet.