aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Percival <cperciva@FreeBSD.org>2007-02-14 22:30:33 +0000
committerColin Percival <cperciva@FreeBSD.org>2007-02-14 22:30:33 +0000
commit7f9161735d514ab289c27c62f4a0358b3862195f (patch)
tree1999feb69a700f1718069e5d3acc843dff60fd0f
parent3adbb43be311e66231e4f5d4461a9cf1d88d729b (diff)
downloadsrc-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--UPDATING4
-rw-r--r--sys/conf/newvers.sh2
-rw-r--r--sys/nfsserver/nfs_serv.c148
-rw-r--r--sys/nfsserver/nfs_srvsubs.c22
-rw-r--r--sys/nfsserver/nfsm_subs.h3
5 files changed, 109 insertions, 70 deletions
diff --git a/UPDATING b/UPDATING
index f844d13530d7..cede9016bbf1 100644
--- a/UPDATING
+++ b/UPDATING
@@ -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); \