From f5429c7c6e76a7e435129573b8f5d42490e39851 Mon Sep 17 00:00:00 2001 From: kib Date: Sun, 10 Feb 2013 10:17:33 +0000 Subject: 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 --- sys/ufs/ffs/ffs_alloc.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'sys/ufs') diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 5ad5775..d462cbb 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -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 -- cgit v1.1