diff options
author | kib <kib@FreeBSD.org> | 2008-12-18 12:01:19 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2008-12-18 12:01:19 +0000 |
commit | 5b3918fe075f9cb971c03bd21b43f199996e2085 (patch) | |
tree | 23a44433d9c47bead44c14342d652064c858aa89 /sys/kern | |
parent | fe785ac856e74cb4f13d1a522b6c71392542707f (diff) | |
download | FreeBSD-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.c | 24 |
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); } |