summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2009-01-08 12:47:30 +0000
committerkib <kib@FreeBSD.org>2009-01-08 12:47:30 +0000
commit97fa3042030bec26a377d4e4e2700219cdb2e668 (patch)
treefc54ddd31bfe1c0f3c6f9aa33f5b3014d49e3e0d
parent6ca4a3c37e3a68ed45f03a98747a6ceb2f93823d (diff)
downloadFreeBSD-src-97fa3042030bec26a377d4e4e2700219cdb2e668.zip
FreeBSD-src-97fa3042030bec26a377d4e4e2700219cdb2e668.tar.gz
Do not call namei() while having another user-controlled vnode
locked. Lookup could attempt to recursively lock that vnode. Do not call vn_start_write(V_WAIT) while vnode is locked, this may result in a deadlock with suspension. vfs_busy() the mountpoint before dropping vnode lock for vnode that was used to look up the mountpoint, to prevent unmount in between. Reported and tested by: pho Reviewed by: rwatson MFC after: 3 weeks
-rw-r--r--sys/kern/vfs_extattr.c49
1 files changed, 31 insertions, 18 deletions
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c
index cd2b9cc..e19a386 100644
--- a/sys/kern/vfs_extattr.c
+++ b/sys/kern/vfs_extattr.c
@@ -87,52 +87,65 @@ extattrctl(td, uap)
AUDIT_ARG(text, attrname);
vfslocked = fnvfslocked = 0;
- /*
- * uap->filename is not always defined. If it is, grab a vnode lock,
- * which VFS_EXTATTRCTL() will later release.
- */
+ mp = NULL;
filename_vp = NULL;
if (uap->filename != NULL) {
- NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF |
- AUDITVNODE2, UIO_USERSPACE, uap->filename, td);
+ NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | AUDITVNODE2,
+ UIO_USERSPACE, uap->filename, td);
error = namei(&nd);
if (error)
return (error);
fnvfslocked = NDHASGIANT(&nd);
filename_vp = nd.ni_vp;
- NDFREE(&nd, NDF_NO_VP_RELE | NDF_NO_VP_UNLOCK);
+ NDFREE(&nd, NDF_NO_VP_RELE);
}
/* uap->path is always defined. */
- NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | AUDITVNODE1, UIO_USERSPACE,
- uap->path, td);
+ NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF | AUDITVNODE1,
+ UIO_USERSPACE, uap->path, td);
error = namei(&nd);
- if (error) {
- if (filename_vp != NULL)
- vput(filename_vp);
+ if (error)
goto out;
- }
vfslocked = NDHASGIANT(&nd);
mp = nd.ni_vp->v_mount;
- error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH);
- NDFREE(&nd, 0);
+ error = vfs_busy(mp, 0);
if (error) {
- if (filename_vp != NULL)
- vput(filename_vp);
+ NDFREE(&nd, 0);
+ mp = NULL;
goto out;
}
+ VOP_UNLOCK(nd.ni_vp, 0);
+ error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH);
+ NDFREE(&nd, NDF_NO_VP_UNLOCK);
+ if (error)
+ goto out;
+ if (filename_vp != NULL) {
+ /*
+ * uap->filename is not always defined. If it is,
+ * grab a vnode lock, which VFS_EXTATTRCTL() will
+ * later release.
+ */
+ error = vn_lock(filename_vp, LK_EXCLUSIVE);
+ if (error) {
+ vn_finished_write(mp_writable);
+ goto out;
+ }
+ }
error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
uap->attrname != NULL ? attrname : NULL, td);
vn_finished_write(mp_writable);
+out:
+ if (mp != NULL)
+ vfs_unbusy(mp);
+
/*
* VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
* so vrele it if it is defined.
*/
if (filename_vp != NULL)
vrele(filename_vp);
-out:
VFS_UNLOCK_GIANT(fnvfslocked);
VFS_UNLOCK_GIANT(vfslocked);
return (error);
OpenPOWER on IntegriCloud