--- a/sys/vfs/nfs/nfsnode.h +++ b/sys/vfs/nfs/nfsnode.h @@ -152,7 +152,7 @@ #define NLMODIFIED 0x0004 /* Client has pending modifications */ #define NWRITEERR 0x0008 /* Flag write errors so close will know */ #define NREMOVED 0x0020 /* Flag possible removal of file */ -#define NUNUSED040 0x0040 +#define NSILLY 0x0040 /* n_un3 currently holds a sillyrename */ #define NQNFSEVICTED 0x0080 /* Has been evicted */ #define NACC 0x0100 /* Special file accessed */ #define NUPD 0x0200 /* Special file updated */ --- a/sys/vfs/nfs/nfs_node.c +++ b/sys/vfs/nfs/nfs_node.c @@ -389,9 +389,19 @@ np = VTONFS(ap->a_vp); if (prtactive && VREFCNT(ap->a_vp) > 1) vprint("nfs_inactive: pushing active", ap->a_vp); - if (ap->a_vp->v_type != VDIR) { + /* + * Discriminate the n_un3 union (nf_silly vs nd_cook) with the + * NSILLY flag, NOT with vp->v_type: the type of a live node is + * mutable at runtime (nfs_loadattrcache -> nfs_setvtype applies + * server-claimed ftype to existing vnodes), so a pending + * sillyrename on a node retyped VREG->VDIR must still be flushed + * here, and a VDIR node with cookies must never be walked as a + * sillyrename. + */ + if (np->n_flag & NSILLY) { sp = np->n_sillyrename; np->n_sillyrename = NULL; + np->n_flag &= ~NSILLY; } else { sp = NULL; } @@ -463,13 +473,32 @@ * large file handle structures that might be associated with * this nfs node. */ - if (vp->v_type == VDIR) { + /* + * Same NSILLY discipline as nfs_inactive(): only walk the cookie + * list if the union actually holds one. A server-side retype of + * a sillyrenamed file to VDIR used to make this loop interpret + * the struct sillyrename (M_NFSREQ) as an nfsdmap list and + * kfree() it (and the s_cred ucred it points at) as M_NFSDIROFF, + * chasing wild pointers from the freed objects. + */ + if (vp->v_type == VDIR && (np->n_flag & NSILLY) == 0) { dp = np->n_cookies.lh_first; while (dp) { dp2 = dp; dp = dp->ndm_list.le_next; kfree((caddr_t)dp2, M_NFSDIROFF); } + } else if (np->n_flag & NSILLY) { + /* unreachable safety net: inactive normally flushes it */ + struct sillyrename *sp = np->n_sillyrename; + + np->n_sillyrename = NULL; + np->n_flag &= ~NSILLY; + if (sp) { + crfree(sp->s_cred); + vrele(sp->s_dvp); + kfree(sp, M_NFSREQ); + } } if (np->n_fhsize > NFS_SMALLFH) { kfree((caddr_t)np->n_fhp, M_NFSBIGFH); --- a/sys/vfs/nfs/nfs_vnops.c +++ b/sys/vfs/nfs/nfs_vnops.c @@ -3036,6 +3036,7 @@ error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, cnp->cn_td, &np); np->n_sillyrename = sp; + np->n_flag |= NSILLY; return (0); bad: vrele(sp->s_dvp);