⬢ DragonFlyBSD Kernel Audit
DF-5831 / fix.diff
← back to finding ↓ download raw
fix for DF-5831: nlookup() retry path NULL-derefs nd->nl_basench for
nlookup_init_raw()/nlookup_init_root()-initialized nd's (NFS server name
lookups via nfs_namei(), webnfs public handle, early-boot mountroot) --
any doretry (ncp generation change from a concurrent rename, ESTALE,
destroyed parent) runs cache_copy(NULL, &nd->nl_nch) at
sys/kern/vfs_nlookup.c:1380 -> Fatal trap 12 at 0x0 in the nfsd process.

Fix: give init_raw/init_root a private, ref-held retry base.  The caller's
ncstart handle cannot be used (nfs_namei drops it immediately after init),
so store a copy in a new nlookupdata field and point nl_basench at it.
nlookup_done() releases it.

Verified in-guest: baseline panics in nfsd within ~2 minutes of the rig;
patched kernel survives the identical rig for the full 300s window twice.

diff --git a/sys/sys/nlookup.h b/sys/sys/nlookup.h
index 1111111..2222222 100644
--- a/sys/sys/nlookup.h
+++ b/sys/sys/nlookup.h
@@ -74,6 +74,7 @@
 	 */
 	struct nchandle nl_nch;		/* result */
 	struct nchandle *nl_basench;	/* start-point directory */
+	struct nchandle nl_basench_store; /* private base (init_raw/_root) */
 	struct nchandle nl_rootnch;	/* root directory */
 	struct nchandle nl_jailnch;	/* jail directory */
 
diff --git a/sys/kern/vfs_nlookup.c b/sys/kern/vfs_nlookup.c
index 1111111..2222222 100644
--- a/sys/kern/vfs_nlookup.c
+++ b/sys/kern/vfs_nlookup.c
@@ -280,6 +280,9 @@ nlookup_init_raw(struct nlookupdata *nd,
 
     if (error == 0) {
 	cache_copy(ncstart, &nd->nl_nch);
+	cache_copy(ncstart, &nd->nl_basench_store);
+	nd->nl_basench = &nd->nl_basench_store;
+
 	cache_copy(&rootnch, &nd->nl_rootnch);
 	cache_copy(&rootnch, &nd->nl_jailnch);
 	nd->nl_cred = crhold(cred);
@@ -324,6 +327,9 @@ nlookup_init_root(struct nlookupdata *nd,
 
     if (error == 0) {
 	cache_copy(ncstart, &nd->nl_nch);
+	cache_copy(ncstart, &nd->nl_basench_store);
+	nd->nl_basench = &nd->nl_basench_store;
+
 	cache_copy(ncroot, &nd->nl_rootnch);
 	cache_copy(ncroot, &nd->nl_jailnch);
 	nd->nl_cred = crhold(cred);
@@ -373,6 +379,10 @@ nlookup_done(struct nlookupdata *nd)
     if (nd->nl_rootnch.ncp)
 	cache_drop_and_cache(&nd->nl_rootnch, 0);
     if (nd->nl_jailnch.ncp)
 	cache_drop_and_cache(&nd->nl_jailnch, 0);
+    if (nd->nl_basench_store.ncp) {
+	cache_drop_and_cache(&nd->nl_basench_store, 0);
+	nd->nl_basench = NULL;
+    }
     if ((nd->nl_flags & NLC_HASBUF) && nd->nl_path) {
 	objcache_put(namei_oc, nd->nl_path);
 	nd->nl_path = NULL;