summaryrefslogtreecommitdiffstats
path: root/sys/nfsserver
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2004-11-11 21:30:52 +0000
committerrwatson <rwatson@FreeBSD.org>2004-11-11 21:30:52 +0000
commitd7f30dee5518a11ee5d88d8f182cfe6947cd1c4b (patch)
treec2d85888433fd3832e862acf5074db8a8e69c17d /sys/nfsserver
parent311bbf454628bc9f76a88948bf3b565a790c4da1 (diff)
downloadFreeBSD-src-d7f30dee5518a11ee5d88d8f182cfe6947cd1c4b.zip
FreeBSD-src-d7f30dee5518a11ee5d88d8f182cfe6947cd1c4b.tar.gz
Correct a bug in nfsrv_create() where a call to nfsrv_access() might
be made holding the NFS server mutex. To clean this up, introduce a version of the function, nfsrv_access_withgiant(), that expects the NFS server mutex to already have been dropped and Giant acquired. Wrap nfsrv_access() around this. This permits callers to more efficiently check access if they're in a code block performing VFS operations, and can be substitited for the nfsrv_access() call that triggered this bug. PR: 73807, 73208 MFC after: 1 week
Diffstat (limited to 'sys/nfsserver')
-rw-r--r--sys/nfsserver/nfs_serv.c90
1 files changed, 52 insertions, 38 deletions
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
index 4a8d9f9..bdad5ef 100644
--- a/sys/nfsserver/nfs_serv.c
+++ b/sys/nfsserver/nfs_serv.c
@@ -137,6 +137,9 @@ struct nfsrvstats nfsrvstats;
SYSCTL_STRUCT(_vfs_nfsrv, NFS_NFSRVSTATS, nfsrvstats, CTLFLAG_RD,
&nfsrvstats, nfsrvstats, "S,nfsrvstats");
+static int nfsrv_access_withgiant(struct vnode *vp, int flags,
+ struct ucred *cred, int rdonly, struct thread *td,
+ int override);
static int nfsrv_access(struct vnode *, int, struct ucred *, int,
struct thread *, int);
static void nfsrvw_coalesce(struct nfsrv_descript *,
@@ -195,8 +198,10 @@ nfsrv3_access(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
goto nfsmout;
}
nfsmode = fxdr_unsigned(u_int32_t, *tl);
+ NFSD_UNLOCK();
+ mtx_lock(&Giant); /* VFS */
if ((nfsmode & NFSV3ACCESS_READ) &&
- nfsrv_access(vp, VREAD, cred, rdonly, td, 0))
+ nfsrv_access_withgiant(vp, VREAD, cred, rdonly, td, 0))
nfsmode &= ~NFSV3ACCESS_READ;
if (vp->v_type == VDIR)
testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND |
@@ -204,17 +209,15 @@ nfsrv3_access(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
else
testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND);
if ((nfsmode & testmode) &&
- nfsrv_access(vp, VWRITE, cred, rdonly, td, 0))
+ nfsrv_access_withgiant(vp, VWRITE, cred, rdonly, td, 0))
nfsmode &= ~testmode;
if (vp->v_type == VDIR)
testmode = NFSV3ACCESS_LOOKUP;
else
testmode = NFSV3ACCESS_EXECUTE;
if ((nfsmode & testmode) &&
- nfsrv_access(vp, VEXEC, cred, rdonly, td, 0))
+ nfsrv_access_withgiant(vp, VEXEC, cred, rdonly, td, 0))
nfsmode &= ~testmode;
- NFSD_UNLOCK();
- mtx_lock(&Giant); /* VFS */
getret = VOP_GETATTR(vp, vap, cred, td);
vput(vp);
mtx_unlock(&Giant); /* VFS */
@@ -857,12 +860,14 @@ nfsrv_read(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
else
error = (vp->v_type == VDIR) ? EISDIR : EACCES;
}
- if (!error) {
- if ((error = nfsrv_access(vp, VREAD, cred, rdonly, td, 1)) != 0)
- error = nfsrv_access(vp, VEXEC, cred, rdonly, td, 1);
- }
NFSD_UNLOCK();
mtx_lock(&Giant); /* VFS */
+ if (!error) {
+ if ((error = nfsrv_access_withgiant(vp, VREAD, cred, rdonly,
+ td, 1)) != 0)
+ error = nfsrv_access_withgiant(vp, VEXEC, cred,
+ rdonly, td, 1);
+ }
getret = VOP_GETATTR(vp, vap, cred, td);
if (!error)
error = getret;
@@ -1497,9 +1502,11 @@ loop1:
} else {
vp = NULL;
}
- if (!error)
- error = nfsrv_access(vp, VWRITE, cred, rdonly, td, 1);
NFSD_UNLOCK();
+ mtx_lock(&Giant); /* VFS */
+ if (!error)
+ error = nfsrv_access_withgiant(vp, VWRITE, cred, rdonly,
+ td, 1);
if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE)
ioflags = IO_NODELOCKED;
else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC)
@@ -1511,7 +1518,6 @@ loop1:
uiop->uio_td = NULL;
uiop->uio_offset = nfsd->nd_off;
uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off;
- mtx_lock(&Giant); /* VFS */
if (uiop->uio_resid > 0) {
mp = mrep;
i = 0;
@@ -1910,8 +1916,8 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
}
} else {
if (vap->va_size != -1) {
- error = nfsrv_access(nd.ni_vp, VWRITE, cred,
- (nd.ni_cnd.cn_flags & RDONLY), td, 0);
+ error = nfsrv_access_withgiant(nd.ni_vp, VWRITE,
+ cred, (nd.ni_cnd.cn_flags & RDONLY), td, 0);
if (!error) {
tempsize = vap->va_size;
VATTR_NULL(vap);
@@ -3370,13 +3376,8 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = NFSERR_BAD_COOKIE;
#endif
}
- if (!error) {
- mtx_unlock(&Giant); /* VFS */
- NFSD_LOCK();
- error = nfsrv_access(vp, VEXEC, cred, rdonly, td, 0);
- NFSD_UNLOCK();
- mtx_lock(&Giant); /* VFS */
- }
+ if (!error)
+ error = nfsrv_access_withgiant(vp, VEXEC, cred, rdonly, td, 0);
if (error) {
vput(vp);
mtx_unlock(&Giant); /* VFS */
@@ -3685,13 +3686,8 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (!error && toff && verf && verf != at.va_filerev)
error = NFSERR_BAD_COOKIE;
#endif
- if (!error) {
- mtx_unlock(&Giant); /* VFS */
- NFSD_LOCK();
- error = nfsrv_access(vp, VEXEC, cred, rdonly, td, 0);
- NFSD_UNLOCK();
- mtx_lock(&Giant); /* VFS */
- }
+ if (!error)
+ error = nfsrv_access_withgiant(vp, VEXEC, cred, rdonly, td, 0);
if (error) {
vput(vp);
mtx_unlock(&Giant); /* VFS */
@@ -4473,7 +4469,8 @@ nfsmout:
* Perform access checking for vnodes obtained from file handles that would
* refer to files already opened by a Unix client. You cannot just use
* vn_writechk() and VOP_ACCESS() for two reasons.
- * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
+ * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write
+ * case.
* 2 - The owner is to be given access irrespective of mode bits for some
* operations, so that processes that chmod after opening a file don't
* break. I don't like this because it opens a security hole, but since
@@ -4482,17 +4479,23 @@ nfsmout:
*
* The exception to rule 2 is EPERM. If a file is IMMUTABLE, VOP_ACCESS()
* will return EPERM instead of EACCESS. EPERM is always an error.
+ *
+ * There are two versions: one to be called while holding Giant (which is
+ * needed due to use of VFS), and the other called with the NFS server lock
+ * (which will be dropped and reacquired). This is necessary because
+ * nfsrv_access checks are required from both classes of contexts.
*/
static int
-nfsrv_access(struct vnode *vp, int flags, struct ucred *cred, int rdonly,
- struct thread *td, int override)
+nfsrv_access_withgiant(struct vnode *vp, int flags, struct ucred *cred,
+ int rdonly, struct thread *td, int override)
{
struct vattr vattr;
int error;
- NFSD_LOCK_ASSERT();
+ GIANT_REQUIRED;
nfsdbprintf(("%s %d\n", __FILE__, __LINE__));
+
if (flags & VWRITE) {
/* Just vn_writechk() changed to check rdonly */
/*
@@ -4517,11 +4520,10 @@ nfsrv_access(struct vnode *vp, int flags, struct ucred *cred, int rdonly,
if (vp->v_vflag & VV_TEXT)
return (ETXTBSY);
}
- NFSD_UNLOCK();
- mtx_lock(&Giant); /* VFS */
+
error = VOP_GETATTR(vp, &vattr, cred, td);
if (error)
- goto out;
+ return (error);
error = VOP_ACCESS(vp, flags, cred, td);
/*
* Allow certain operations for the owner (reads and writes
@@ -4529,9 +4531,21 @@ nfsrv_access(struct vnode *vp, int flags, struct ucred *cred, int rdonly,
*/
if (override && error == EACCES && cred->cr_uid == vattr.va_uid)
error = 0;
-out:
- NFSD_UNLOCK_ASSERT();
+ return (error);
+}
+
+static int
+nfsrv_access(struct vnode *vp, int flags, struct ucred *cred, int rdonly,
+ struct thread *td, int override)
+{
+ int error;
+
+ NFSD_LOCK_ASSERT();
+
+ NFSD_UNLOCK();
+ mtx_lock(&Giant); /* VFS */
+ error = nfsrv_access_withgiant(vp, flags, cred, rdonly, td, override);
mtx_unlock(&Giant); /* VFS */
NFSD_LOCK();
- return error;
+ return (error);
}
OpenPOWER on IntegriCloud