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
This commit is contained in:
Konstantin Belousov 2008-12-18 12:01:19 +00:00
parent 2cfddad734
commit 548066ea66

View File

@ -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);
}