summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2008-12-18 12:01:19 +0000
committerkib <kib@FreeBSD.org>2008-12-18 12:01:19 +0000
commit5b3918fe075f9cb971c03bd21b43f199996e2085 (patch)
tree23a44433d9c47bead44c14342d652064c858aa89 /sys/kern
parentfe785ac856e74cb4f13d1a522b6c71392542707f (diff)
downloadFreeBSD-src-5b3918fe075f9cb971c03bd21b43f199996e2085.zip
FreeBSD-src-5b3918fe075f9cb971c03bd21b43f199996e2085.tar.gz
The quotactl, statfs and fstatfs syscall implementations may dereference
NULL pointer to struct mount if the looked up vnode is reclaimed. Also, these syscalls only mnt_ref() the mp, still allowing it to be unmounted; only struct mount memory is kept from being reused. Lock the vnode when doing name lookup, then reference its mount point, unlock the vnode and vfs_busy the mountpoint. This sequence shall take care of both races. Reported and tested by: pho Discussed with: attilio MFC after: 1 month
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/vfs_syscalls.c24
1 files changed, 18 insertions, 6 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index e4f4f8b..198009d 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -200,19 +200,21 @@ quotactl(td, uap)
AUDIT_ARG(uid, uap->uid);
if (jailed(td->td_ucred) && !prison_quotas)
return (EPERM);
- NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE | AUDITVNODE1,
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
UIO_USERSPACE, uap->path, td);
if ((error = namei(&nd)) != 0)
return (error);
vfslocked = NDHASGIANT(&nd);
NDFREE(&nd, NDF_ONLY_PNBUF);
mp = nd.ni_vp->v_mount;
- if ((error = vfs_busy(mp, 0))) {
- vrele(nd.ni_vp);
+ vfs_ref(mp);
+ vput(nd.ni_vp);
+ error = vfs_busy(mp, 0);
+ vfs_rel(mp);
+ if (error) {
VFS_UNLOCK_GIANT(vfslocked);
return (error);
}
- vrele(nd.ni_vp);
error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, td);
vfs_unbusy(mp);
VFS_UNLOCK_GIANT(vfslocked);
@@ -306,6 +308,12 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg,
vfs_ref(mp);
NDFREE(&nd, NDF_ONLY_PNBUF);
vput(nd.ni_vp);
+ error = vfs_busy(mp, 0);
+ vfs_rel(mp);
+ if (error) {
+ VFS_UNLOCK_GIANT(vfslocked);
+ return (error);
+ }
#ifdef MAC
error = mac_mount_check_stat(td->td_ucred, mp);
if (error)
@@ -329,7 +337,7 @@ kern_statfs(struct thread *td, char *path, enum uio_seg pathseg,
}
*buf = *sp;
out:
- vfs_rel(mp);
+ vfs_unbusy(mp);
VFS_UNLOCK_GIANT(vfslocked);
if (mtx_owned(&Giant))
printf("statfs(%d): %s: %d\n", vfslocked, path, error);
@@ -391,6 +399,10 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf)
error = EBADF;
goto out;
}
+ error = vfs_busy(mp, 0);
+ vfs_rel(mp);
+ if (error)
+ goto out;
#ifdef MAC
error = mac_mount_check_stat(td->td_ucred, mp);
if (error)
@@ -415,7 +427,7 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf)
*buf = *sp;
out:
if (mp)
- vfs_rel(mp);
+ vfs_unbusy(mp);
VFS_UNLOCK_GIANT(vfslocked);
return (error);
}
OpenPOWER on IntegriCloud