diff options
author | kib <kib@FreeBSD.org> | 2013-02-10 10:17:33 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2013-02-10 10:17:33 +0000 |
commit | f5429c7c6e76a7e435129573b8f5d42490e39851 (patch) | |
tree | ad5cb6a2e87290604c9e5fca2c5713a046c6474c /sys/ufs | |
parent | cde657f5b102f59e8b6ea59855aeb883fac1a7dd (diff) | |
download | FreeBSD-src-f5429c7c6e76a7e435129573b8f5d42490e39851.zip FreeBSD-src-f5429c7c6e76a7e435129573b8f5d42490e39851.tar.gz |
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
Diffstat (limited to 'sys/ufs')
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 26 |
1 files changed, 23 insertions, 3 deletions
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 |