diff options
author | Colin Percival <cperciva@FreeBSD.org> | 2007-02-14 22:30:33 +0000 |
---|---|---|
committer | Colin Percival <cperciva@FreeBSD.org> | 2007-02-14 22:30:33 +0000 |
commit | 7f9161735d514ab289c27c62f4a0358b3862195f (patch) | |
tree | 1999feb69a700f1718069e5d3acc843dff60fd0f | |
parent | 3adbb43be311e66231e4f5d4461a9cf1d88d729b (diff) | |
download | src-releng/6.0.tar.gz src-releng/6.0.zip |
Correct problems with locking, namei leakage, and symlinkreleng/6.0
creation in the NFS subsystem.
Approved by: so (cperciva)
Submitted by: re (hrs)
Errata: FreeBSD-EN-07:01.nfs
Notes
Notes:
svn path=/releng/6.0/; revision=166720
-rw-r--r-- | UPDATING | 4 | ||||
-rw-r--r-- | sys/conf/newvers.sh | 2 | ||||
-rw-r--r-- | sys/nfsserver/nfs_serv.c | 148 | ||||
-rw-r--r-- | sys/nfsserver/nfs_srvsubs.c | 22 | ||||
-rw-r--r-- | sys/nfsserver/nfsm_subs.h | 3 |
5 files changed, 109 insertions, 70 deletions
@@ -8,6 +8,10 @@ Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. +20070214: p18 FreeBSD-EN-07:01.nfs + Correct problems with locking, namei leakage, and symlink + creation in the NFS subsystem. + 20070111: p17 FreeBSD-SA-07:01.jail Correct jail rc.d script privilege escalation. diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh index 7287488d2516..6fca3108cbe7 100644 --- a/sys/conf/newvers.sh +++ b/sys/conf/newvers.sh @@ -32,7 +32,7 @@ TYPE="FreeBSD" REVISION="6.0" -BRANCH="RELEASE-p17" +BRANCH="RELEASE-p18" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c index 4a028fa7d265..38e74663b145 100644 --- a/sys/nfsserver/nfs_serv.c +++ b/sys/nfsserver/nfs_serv.c @@ -569,6 +569,10 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, error = lookup(&ind); ind.ni_dvp = NULL; + if (ind.ni_cnd.cn_flags & GIANTHELD) { + mtx_unlock(&Giant); + ind.ni_cnd.cn_flags &= ~GIANTHELD; + } if (error == 0) { /* @@ -599,15 +603,9 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, } } - if (dirp) { - vrele(dirp); - dirp = NULL; - } - /* * Resources at this point: * ndp->ni_vp may not be NULL - * */ if (error) { @@ -621,15 +619,6 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, } /* - * Clear out some resources prior to potentially blocking. This - * is not as critical as ni_dvp resources in other routines, but - * it helps. - */ - vrele(ndp->ni_startdir); - ndp->ni_startdir = NULL; - NDFREE(&nd, NDF_ONLY_PNBUF); - - /* * Get underlying attribute, then release remaining resources ( for * the same potential blocking reason ) and reply. */ @@ -641,8 +630,12 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, error = VOP_GETATTR(vp, vap, cred, td); vput(vp); - mtx_unlock(&Giant); /* VFS */ + vrele(ndp->ni_startdir); + vrele(dirp); ndp->ni_vp = NULL; + ndp->ni_startdir = NULL; + dirp = NULL; + mtx_unlock(&Giant); /* VFS */ NFSD_LOCK(); nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3)); if (error) { @@ -662,17 +655,19 @@ nfsrv_lookup(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, nfsmout: NFSD_LOCK_ASSERT(); - NFSD_UNLOCK(); - mtx_lock(&Giant); /* VFS */ - if (dirp) - vrele(dirp); + if (ndp->ni_vp || dirp || ndp->ni_startdir) { + NFSD_UNLOCK(); + mtx_lock(&Giant); /* VFS */ + if (ndp->ni_vp) + vput(ndp->ni_vp); + if (dirp) + vrele(dirp); + if (ndp->ni_startdir) + vrele(ndp->ni_startdir); + mtx_unlock(&Giant); /* VFS */ + NFSD_LOCK(); + } NDFREE(&nd, NDF_ONLY_PNBUF); - if (ndp->ni_startdir) - vrele(ndp->ni_startdir); - if (ndp->ni_vp) - vput(ndp->ni_vp); - mtx_unlock(&Giant); /* VFS */ - NFSD_LOCK(); return (error); } @@ -1924,6 +1919,10 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, error = lookup(&nd); nd.ni_dvp = NULL; + if (nd.ni_cnd.cn_flags & GIANTHELD) { + mtx_unlock(&Giant); + nd.ni_cnd.cn_flags &= ~GIANTHELD; + } if (error) goto ereply; @@ -2004,13 +2003,6 @@ nfsmout: NFSD_LOCK_ASSERT(); NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ - if (nd.ni_startdir) { - vrele(nd.ni_startdir); - nd.ni_startdir = NULL; - } - if (dirp) - vrele(dirp); - NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp) { if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); @@ -2019,6 +2011,13 @@ nfsmout: } if (nd.ni_vp) vput(nd.ni_vp); + if (nd.ni_startdir) { + vrele(nd.ni_startdir); + nd.ni_startdir = NULL; + } + if (dirp) + vrele(dirp); + NDFREE(&nd, NDF_ONLY_PNBUF); vn_finished_write(mp); mtx_unlock(&Giant); /* VFS */ NFSD_LOCK(); @@ -2092,6 +2091,8 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, tl = nfsm_dissect_nonblock(u_int32_t *, NFSX_UNSIGNED); vtyp = nfsv3tov_type(*tl); if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) { + NFSD_UNLOCK(); + mtx_lock(&Giant); /* VFS */ error = NFSERR_BADTYPE; goto out; } @@ -2108,6 +2109,8 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, * Iff doesn't exist, create it. */ if (nd.ni_vp) { + NFSD_UNLOCK(); + mtx_lock(&Giant); /* VFS */ error = EEXIST; goto out; } @@ -2146,6 +2149,10 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, error = lookup(&nd); nd.ni_dvp = NULL; + if (nd.ni_cnd.cn_flags & GIANTHELD) { + mtx_unlock(&Giant); + nd.ni_cnd.cn_flags &= ~GIANTHELD; + } if (error) goto out; @@ -2158,18 +2165,6 @@ nfsrv_mknod(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, */ out: NFSD_UNLOCK_ASSERT(); - if (nd.ni_startdir) { - vrele(nd.ni_startdir); - nd.ni_startdir = NULL; - } - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_dvp) { - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - nd.ni_dvp = NULL; - } vp = nd.ni_vp; if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); @@ -2178,11 +2173,23 @@ out: if (!error) error = VOP_GETATTR(vp, vap, cred, td); } + if (nd.ni_dvp) { + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + nd.ni_dvp = NULL; + } if (vp) { vput(vp); vp = NULL; nd.ni_vp = NULL; } + if (nd.ni_startdir) { + vrele(nd.ni_startdir); + nd.ni_startdir = NULL; + } + NDFREE(&nd, NDF_ONLY_PNBUF); if (dirp) { vn_lock(dirp, LK_EXCLUSIVE | LK_RETRY, td); diraft_ret = VOP_GETATTR(dirp, &diraft, cred, td); @@ -2210,11 +2217,6 @@ nfsmout: NFSD_LOCK_ASSERT(); NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ - if (dirp) - vrele(dirp); - if (nd.ni_startdir) - vrele(nd.ni_startdir); - NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp) { if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); @@ -2223,6 +2225,11 @@ nfsmout: } if (nd.ni_vp) vput(nd.ni_vp); + if (dirp) + vrele(dirp); + if (nd.ni_startdir) + vrele(nd.ni_startdir); + NDFREE(&nd, NDF_ONLY_PNBUF); vn_finished_write(mp); mtx_unlock(&Giant); /* VFS */ NFSD_LOCK(); @@ -2519,8 +2526,8 @@ out: tond.ni_dvp = NULL; tond.ni_vp = NULL; if (error) { - fromnd.ni_cnd.cn_flags &= ~HASBUF; - tond.ni_cnd.cn_flags &= ~HASBUF; + NDFREE(&fromnd, NDF_ONLY_PNBUF); + NDFREE(&tond, NDF_ONLY_PNBUF); } } else { if (error == -1) @@ -2573,11 +2580,6 @@ nfsmout: NFSD_LOCK_ASSERT(); NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ - if (tdirp) - vrele(tdirp); - if (tond.ni_startdir) - vrele(tond.ni_startdir); - NDFREE(&tond, NDF_ONLY_PNBUF); if (tond.ni_dvp) { if (tond.ni_dvp == tond.ni_vp) vrele(tond.ni_dvp); @@ -2586,7 +2588,11 @@ nfsmout: } if (tond.ni_vp) vput(tond.ni_vp); - + if (tdirp) + vrele(tdirp); + if (tond.ni_startdir) + vrele(tond.ni_startdir); + NDFREE(&tond, NDF_ONLY_PNBUF); /* * Clear out fromnd related fields */ @@ -2747,8 +2753,6 @@ nfsmout: NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ NDFREE(&nd, NDF_ONLY_PNBUF); - if (dirp) - vrele(dirp); if (vp) vput(vp); if (nd.ni_dvp) { @@ -2757,6 +2761,8 @@ nfsmout: else vput(nd.ni_dvp); } + if (dirp) + vrele(dirp); if (nd.ni_vp) vrele(nd.ni_vp); vn_finished_write(mp); @@ -2815,6 +2821,12 @@ nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, v3, &dirfor, &dirfor_ret, td, FALSE); + if (error == 0) { + VATTR_NULL(vap); + if (v3) + nfsm_srvsattr(vap); + nfsm_srvpathsiz(len2); + } NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ if (dirp && !v3) { @@ -2824,10 +2836,6 @@ nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, if (error) goto out; - VATTR_NULL(vap); - if (v3) - nfsm_srvsattr(vap); - nfsm_srvpathsiz(len2); MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK); iv.iov_base = pathcp; iv.iov_len = len2; @@ -2884,6 +2892,10 @@ nfsrv_symlink(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp, error = lookup(&nd); nd.ni_dvp = NULL; + if (nd.ni_cnd.cn_flags & GIANTHELD) { + mtx_unlock(&Giant); + nd.ni_cnd.cn_flags &= ~GIANTHELD; + } if (error == 0) { bzero((caddr_t)fhp, sizeof(nfh)); @@ -3113,8 +3125,6 @@ nfsmout: NFSD_LOCK_ASSERT(); NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ - if (dirp) - vrele(dirp); if (nd.ni_dvp) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp && vpexcl) @@ -3128,6 +3138,8 @@ nfsmout: else vrele(nd.ni_vp); } + if (dirp) + vrele(dirp); vn_finished_write(mp); mtx_unlock(&Giant); /* VFS */ NFSD_LOCK(); @@ -3255,8 +3267,6 @@ nfsmout: NFSD_UNLOCK(); mtx_lock(&Giant); /* VFS */ NDFREE(&nd, NDF_ONLY_PNBUF); - if (dirp) - vrele(dirp); if (nd.ni_dvp) { if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); @@ -3265,6 +3275,8 @@ nfsmout: } if (nd.ni_vp) vput(nd.ni_vp); + if (dirp) + vrele(dirp); vn_finished_write(mp); mtx_unlock(&Giant); /* VFS */ diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c index 912b5a7bf20b..41adae0f0da1 100644 --- a/sys/nfsserver/nfs_srvsubs.c +++ b/sys/nfsserver/nfs_srvsubs.c @@ -875,6 +875,10 @@ nfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len, } if (!lockleaf) cnp->cn_flags &= ~LOCKLEAF; + if (cnp->cn_flags & GIANTHELD) { + mtx_unlock(&Giant); + cnp->cn_flags &= ~GIANTHELD; + } /* * nfs_namei() guarentees that fields will not contain garbage @@ -1331,6 +1335,24 @@ nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos) return 0; } +int +nfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos) +{ + u_int32_t *tl; + + NFSD_LOCK_DONTCARE(); + + tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos); + if (tl == NULL) + return EBADRPC; + *s = fxdr_unsigned(int32_t, *tl); + if (*s > m) + return NFSERR_NAMETOL; + if (*s < 0) + return EBADRPC; + return 0; +} + void nfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp, char **bp, char **be, caddr_t bpos, int droplock) diff --git a/sys/nfsserver/nfsm_subs.h b/sys/nfsserver/nfsm_subs.h index 82e7e25bb284..b605c5df7b20 100644 --- a/sys/nfsserver/nfsm_subs.h +++ b/sys/nfsserver/nfsm_subs.h @@ -74,6 +74,7 @@ int nfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos); int nfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos); +int nfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos); int nfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md, caddr_t *dpos); int nfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos); @@ -101,7 +102,7 @@ do { \ #define nfsm_srvpathsiz(s) \ do { \ int t1; \ - t1 = nfsm_srvnamesiz_xx(&(s), NFS_MAXPATHLEN, &md, &dpos); \ + t1 = nfsm_srvnamesiz0_xx(&(s), NFS_MAXPATHLEN, &md, &dpos); \ if (t1) { \ error = t1; \ nfsm_reply(0); \ |