Fix several unsafe pointer dereferences in the buffered_write()

function, implementing the sysctl vfs.ffs.set_bufoutput (not used in
the tree yet).

- The current directory vnode dereference is unsafe since fd_cdir
  could be changed and unreferenced, lock the filedesc around and vref
  the fd_cdir.
- The VTOI() conversion of the fd_cdir is unsafe without first
  checking that the vnode is indeed from an FFS mount, otherwise
  the code dereferences a random memory.
- The cdir could be reclaimed from under us, lock it around the
  checks.
- The type of the fp vnode might be not a disk, or it might have
  changed while the thread was in flight, check the type.

Reviewed and tested by:	mckusick
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2013-02-10 10:17:33 +00:00
parent 5e60cb948e
commit 9604a7f1b8

View File

@ -2906,10 +2906,11 @@ buffered_write(fp, uio, active_cred, flags, td)
int flags;
struct thread *td;
{
struct vnode *devvp;
struct vnode *devvp, *vp;
struct inode *ip;
struct buf *bp;
struct fs *fs;
struct filedesc *fdp;
int error;
daddr_t lbn;
@ -2920,10 +2921,29 @@ buffered_write(fp, uio, active_cred, flags, td)
* within the filesystem being written. Yes, this is an ugly hack.
*/
devvp = fp->f_vnode;
ip = VTOI(td->td_proc->p_fd->fd_cdir);
if (ip->i_devvp != devvp)
if (!vn_isdisk(devvp, NULL))
return (EINVAL);
fdp = td->td_proc->p_fd;
FILEDESC_SLOCK(fdp);
vp = fdp->fd_cdir;
vref(vp);
FILEDESC_SUNLOCK(fdp);
vn_lock(vp, LK_SHARED | LK_RETRY);
/*
* Check that the current directory vnode indeed belongs to
* UFS before trying to dereference UFS-specific v_data fields.
*/
if (vp->v_op != &ffs_vnodeops1 && vp->v_op != &ffs_vnodeops2) {
vput(vp);
return (EINVAL);
}
ip = VTOI(vp);
if (ip->i_devvp != devvp) {
vput(vp);
return (EINVAL);
}
fs = ip->i_fs;
vput(vp);
foffset_lock_uio(fp, uio, flags);
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
#ifdef DEBUG