Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Mar 2012 15:51:30 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Peter Holm <pho@FreeBSD.org>
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:  <20120309145110.D1365@besplex.bde.org>
In-Reply-To: <201203081249.q28Cn9ed045648@svn.freebsd.org>
References:  <201203081249.q28Cn9ed045648@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
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.

> 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.

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



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