Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Mar 2012 07:40:36 +0100
From:      Peter Holm <peter@holm.cc>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r232692 - head/sys/ufs/ffs
Message-ID:  <20120309064036.GA40029@x2.osted.lan>
In-Reply-To: <20120309145110.D1365@besplex.bde.org>
References:  <201203081249.q28Cn9ed045648@svn.freebsd.org> <20120309145110.D1365@besplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, Mar 09, 2012 at 03:51:30PM +1100, Bruce Evans wrote:
> On Thu, 8 Mar 2012, Peter Holm wrote:
> 
> >Log:
> > syscall() fuzzing can trigger this panic. Return EINVAL instead.
> >
> > MFC after:	1 week
> 
> If so, then, this is not the place to hide the bug.
> 

OK

> >Modified: head/sys/ufs/ffs/ffs_vnops.c
> >==============================================================================
> >--- head/sys/ufs/ffs/ffs_vnops.c	Thu Mar  8 11:05:53 2012 (r232691)
> >+++ head/sys/ufs/ffs/ffs_vnops.c	Thu Mar  8 12:49:08 2012 (r232692)
> >@@ -464,11 +464,11 @@ ffs_read(ap)
> >	} else if (vp->v_type != VREG && vp->v_type != VDIR)
> >		panic("ffs_read: type %d",  vp->v_type);
> >#endif
> >+	if (uio->uio_resid < 0 || uio->uio_offset < 0)
> >+		return (EINVAL);
> >	orig_resid = uio->uio_resid;
> >-	KASSERT(orig_resid >= 0, ("ffs_read: uio->uio_resid < 0"));
> >	if (orig_resid == 0)
> >		return (0);
> >-	KASSERT(uio->uio_offset >= 0, ("ffs_read: uio->uio_offset < 0"));
> >	fs = ip->i_fs;
> >	if (uio->uio_offset < ip->i_size &&
> >	    uio->uio_offset >= fs->fs_maxfilesize)
> 
> All file systems are supposed to copy ffs here.  The ones that actually
> do are still correct here.
> 
> The code that enforces a non-negative uio_resid for read(2) is:
> 
> % #ifndef _SYS_SYSPROTO_H_
> % struct read_args {
> % 	int	fd;
> % 	void	*buf;
> % 	size_t	nbyte;
> % };
> % #endif
> % int
> % sys_read(td, uap)
> % 	struct thread *td;
> % 	struct read_args *uap;
> % {
> % 	struct uio auio;
> % 	struct iovec aiov;
> % 	int error;
> % 
> % 	if (uap->nbyte > IOSIZE_MAX)
> % 		return (EINVAL);
> 
> uap->nbyte is unsigned, so it is never negative.  IOSIZE_MAX is either
> INT_MAX or SSIZE_MAX.
> 
> % 	aiov.iov_base = uap->buf;
> % 	aiov.iov_len = uap->nbyte;
> 
> iov_len has type size_t, so it can represent any value of nbytes, since
> that has type size_t too.
> 
> % 	auio.uio_iov = &aiov;
> % 	auio.uio_iovcnt = 1;
> % 	auio.uio_resid = uap->nbyte;
> 
> uio_resid has type ssize_t, so it can represent any value of nbyte less
> than IOSIZE_MAX.  Thus no overflow occurs on this assignment, else there
> would be undefined behaviour and perhaps a negative value here.
> 
> % 	auio.uio_segflg = UIO_USERSPACE;
> % 	error = kern_readv(td, uap->fd, &auio);
> % 	return(error);
> % }
> 
> kib is supposed to have been careful converting the old int types and
> INT_MAX limit to ssize_t types and SSIZE_MAX limit, so that negative
> values remain impossible.  They should be so impossible that asserting
> that they don't happen in VOPs is as useful as asserting that parity
> errors in the CPU don't happen, but if one happens then the fix is not
> to remove the assertion.
> 
> Negative offsets seem to be disallowed correctly in sys_lseek() and
> sys_preadv().
> 
> syscall() can be used to pass weirder args than usual, but I don't
> know any way to reach ffs_read() without going through the checking
> in sys_lseek() or sys_preadv().  A stack trace of the panic would be
> useful for showing how it got through.
> 

This specific problem here was discover by:

        for (i = 0; i < 2000; i++) {
                if ((dirp = opendir(path)) == NULL)
                        break;
                bcopy(dirp, &fuzz, sizeof(fuzz));
                fuzz.dd_len = arc4random();
                readdir(&fuzz);
                closedir(dirp);
        }

http://people.freebsd.org/~pho/stress/log/kostik478.txt

> There seems to be no checking of the non-negativeness of the arg for
> mmap().  That is almost correct, since the offset for mmap() is for
> memory, so off_t and its signedness are bogus anyway.
> 
> sys_sendfile() checks.
> 
> Bruce

-- 
Peter



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120309064036.GA40029>