diff options
author | Ken Smith <kensmith@FreeBSD.org> | 2005-01-05 03:35:00 +0000 |
---|---|---|
committer | Ken Smith <kensmith@FreeBSD.org> | 2005-01-05 03:35:00 +0000 |
commit | a750b6376a2b63602115e458bea53cc9b7bcb37b (patch) | |
tree | 39302fab0d81ebf681dd350b50189753a334dd5a | |
parent | 79cea67456080f0096dd0341c5256829cf8884d8 (diff) |
MFC of src/sys/nfsserver/nfs_serv.c rev 1.147.2.3:
Merge nfs_serv.c:1.151 from HEAD to RELENG_5:
date: 2004/11/11 21:30:52; author: rwatson; state: Exp; lines: +52 -38
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
Approved by: so (nectar)
Work done by: rwatson
Errata Notice: FreeBSD-EN-05:01.nfs
Notes
Notes:
svn path=/releng/5.3/; revision=139697
-rw-r--r-- | UPDATING | 5 | ||||
-rw-r--r-- | sys/conf/newvers.sh | 2 | ||||
-rw-r--r-- | sys/nfsserver/nfs_serv.c | 90 |
3 files changed, 58 insertions, 39 deletions
@@ -8,6 +8,11 @@ Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. Important recent entries: 20040724 (default X changes). +20050103: p3 FreeBSD-EN-05:01.nfs + Correct a bug in nfsrv_create() where a call to nfsrv_access() + might be made while holding the NFS server mutex, which resulted + in kernel panics under certain load patterns. + 20041201: p2 FreeBSD-SA-04:17.procfs Fix a tainted pointer dereference in procfs(5) and linprocfs(5) which could allow a local attacker to panic a system and/or read diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh index 5a2e3aa6a4d5..37d90ee10576 100644 --- a/sys/conf/newvers.sh +++ b/sys/conf/newvers.sh @@ -32,7 +32,7 @@ TYPE="FreeBSD" REVISION="5.3" -BRANCH="RELEASE-p2" +BRANCH="RELEASE-p3" RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c index e55e2d63263e..5115be41c15a 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); } |