diff options
author | 2009-12-17 16:30:45 +0000 | |
---|---|---|
committer | 2009-12-17 16:30:45 +0000 | |
commit | d4d95a55b98940749a0aceb91c2b7b820979dea0 (patch) | |
tree | 2315c30dc8411e22b73e5b1b473ce02b1c47a720 | |
parent | be slightly more paranoid about shell stuff we run. (diff) | |
download | wireguard-openbsd-d4d95a55b98940749a0aceb91c2b7b820979dea0.tar.xz wireguard-openbsd-d4d95a55b98940749a0aceb91c2b7b820979dea0.zip |
This fixes a case where we could panic on a null deref with a bad vnode
in nfs_inactive, on a reboot.
The core of the problem was in nfs_nget, when we lose the race to put a new
nfsnode in the tree, we have previously allocated a vnode, which getnewvnode
has done an insmntque into the nfs mp's mntlist. The problem being we then
try again with a new vnode, abandoning this one on the mntlist, leaving
junk there for us to die on when we unmount.
This introduces VLARVAL - so we can indicate in a vnode that the higher
level stuff hiding in v_data is incompletely set up. This flag is then
used by nfs to deal with a halfway set up vnode and release it correctly.
analysis and bogus fix by art@, correct fix by me after serveral failed
attempts and much painful testing by krw@, good suggestions by tedu and miod
ok krw@ oga@ thib@ blambert@ art@
-rw-r--r-- | sys/nfs/nfs_node.c | 38 | ||||
-rw-r--r-- | sys/sys/vnode.h | 5 |
2 files changed, 35 insertions, 8 deletions
diff --git a/sys/nfs/nfs_node.c b/sys/nfs/nfs_node.c index 94e7fd6640d..44efb9ed504 100644 --- a/sys/nfs/nfs_node.c +++ b/sys/nfs/nfs_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nfs_node.c,v 1.50 2009/12/15 17:04:00 beck Exp $ */ +/* $OpenBSD: nfs_node.c,v 1.51 2009/12/17 16:30:47 beck Exp $ */ /* $NetBSD: nfs_node.c,v 1.16 1996/02/18 11:53:42 fvdl Exp $ */ /* @@ -115,15 +115,17 @@ loop: */ rw_exit_write(&nfs_hashlock); error = getnewvnode(VT_NFS, mnt, nfsv2_vnodeop_p, &nvp); + /* note that we don't have this vnode set up completely yet */ rw_enter_write(&nfs_hashlock); if (error) { *npp = NULL; rw_exit_write(&nfs_hashlock); return (error); } - + nvp->v_flag |= VLARVAL; np = RB_FIND(nfs_nodetree, &nmp->nm_ntree, &find); if (np != NULL) { + vgone(nvp); rw_exit_write(&nfs_hashlock); goto loop; } @@ -131,6 +133,8 @@ loop: vp = nvp; np = pool_get(&nfs_node_pool, PR_WAITOK | PR_ZERO); vp->v_data = np; + /* we now have an nfsnode on this vnode */ + vp->v_flag &= ~VLARVAL; np->n_vnode = vp; rw_init(&np->n_commitlock, "nfs_commitlk"); @@ -161,14 +165,25 @@ int nfs_inactive(void *v) { struct vop_inactive_args *ap = v; - struct nfsnode *np = VTONFS(ap->a_vp); + struct nfsnode *np; struct sillyrename *sp; #ifdef DIAGNOSTIC if (prtactive && ap->a_vp->v_usecount != 0) vprint("nfs_inactive: pushing active", ap->a_vp); #endif - + if (ap->a_vp->v_flag & VLARVAL) + /* + * vnode was incompletely set up, just return + * as we are throwing it away. + */ + return(0); +#ifdef DIAGNOSTIC + if (ap->a_vp->v_data == NULL) + panic("NULL v_data (no nfsnode set up?) in vnode %p\n", + ap->a_vp); +#endif + np = VTONFS(ap->a_vp); if (ap->a_vp->v_type != VDIR) { sp = np->n_sillyrename; np->n_sillyrename = NULL; @@ -198,14 +213,25 @@ nfs_reclaim(void *v) { struct vop_reclaim_args *ap = v; struct vnode *vp = ap->a_vp; - struct nfsmount *nmp = VFSTONFS(vp->v_mount); + struct nfsmount *nmp; struct nfsnode *np = VTONFS(vp); #ifdef DIAGNOSTIC if (prtactive && vp->v_usecount != 0) vprint("nfs_reclaim: pushing active", vp); #endif - + if (ap->a_vp->v_flag & VLARVAL) + /* + * vnode was incompletely set up, just return + * as we are throwing it away. + */ + return(0); +#ifdef DIAGNOSTIC + if (ap->a_vp->v_data == NULL) + panic("NULL v_data (no nfsnode set up?) in vnode %p\n", + ap->a_vp); +#endif + nmp = VFSTONFS(vp->v_mount); rw_enter_write(&nfs_hashlock); RB_REMOVE(nfs_nodetree, &nmp->nm_ntree, np); rw_exit_write(&nfs_hashlock); diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 019e28a251a..76eea0e99d8 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vnode.h,v 1.103 2009/08/12 16:42:24 beck Exp $ */ +/* $OpenBSD: vnode.h,v 1.104 2009/12/17 16:30:45 beck Exp $ */ /* $NetBSD: vnode.h,v 1.38 1996/02/29 20:59:05 cgd Exp $ */ /* @@ -136,10 +136,11 @@ struct vnode { #define VXWANT 0x0200 /* process is waiting for vnode */ #define VCLONED 0x0400 /* vnode was cloned */ #define VALIASED 0x0800 /* vnode has an alias */ +#define VLARVAL 0x1000 /* vnode data not yet set up by higher level */ #define VLOCKSWORK 0x4000 /* FS supports locking discipline */ #define VCLONE 0x8000 /* vnode is a clone */ #define VBITS "\010\001ROOT\002TEXT\003SYSTEM\004ISTTY\010XLOCK" \ - "\011XWANT\013ALIASED\016LOCKSWORK\017CLONE" + "\011XWANT\013ALIASED\014LARVAL\016LOCKSWORK\017CLONE" /* * (v_bioflag) Flags that may be manipulated by interrupt handlers |