On 01/27/18 11:14, Warner Losh wrote:


On Jan 27, 2018 8:17 AM, "Pedro Giffuni" <p...@freebsd.org <mailto:p...@freebsd.org>> wrote:



    On 01/26/18 06:36, Bruce Evans wrote:

        On Thu, 25 Jan 2018, Pedro Giffuni wrote:

            On 25/01/2018 14:24, Bruce Evans wrote:

                ...
                This code only works because (if?) nfs is the only
                caller and nfs never
                passes insane values.


            I am starting to think that we should simply match
            uio_resid and set it to ssize_t.
            Returning the value to int is certainly not the solution.


        Of course using the correct type (int) is part of the solution.

        uio_must be checked before it is used for cookies, and after
        checking it, it
        is small so it fits easily in an int.  It must also checked to
        be nonnegative,
        so that it doesn't suffer unsigned poisoning when it is
        promoted, so it would
        also fit in a u_int, but using u_int to store it is silly as
        using 1U instead
        of 1 for a count of 1.

        The bounds checking is something like:

            if (ap->uio_resid < 0)
                ap->uio_resid = 0;
            if (ap->a_ncookies != NULL) {
                if (ap->uio_resid >= 64 * 1024)
                    ap->uio_resid = 64 * 1024;
                ncookies = ap->uio_resid;
            }

        This checks for negative values for all cases and converts to
        0 (EOF) to
        preserve historical behaviour for the syscall case and to
        avoid overflow
        for the cookies case (in case the caller is buggy). The
        correct handling
        is to return EINVAL, but EOF is good enough.

        In the syscall case, uio_resid can be up to SSIZE_MAX, so
        don't check it
        or corrupt it by assigning it to an int or u_int.

        Limit uio_resid from above only in the cookies case.  The
        final limit should
        be about 128K (whatever nfs uses) or maybe 1M. Don't return
        EINVAL above
        the limit, since nfs probably wouldn't know how to handle that
        (by retrying
        with a smaller size).  Test its handling of short counts
        instead. It is
        expected than nfs asks for 128K and we supply at most 64K. 
        The supply is
        always reduced at EOF.  Hopefully nfs doesn't treat the short
        count as EOF.
        It should retry until we supply 0.

    Hmm ...

    We have never checked the upper bound there, which doesn't mean it
    was right.
    I found MAXPHYS, which seems a more reasonable limit used in the
    kernel for uio_resid.

    I am checking the patch compiles and doesn't give surprises.


MAXPHYS is almost the right thing to check. There's per device limits for normal I/O, but that doesn't seem to be a strict limit for readdir.


OK... new patch, this time again trying to sanitize only ncookies (which can be int or u_int, doesn't matter to me).

Pedro.

Index: sys/fs/ext2fs/ext2_lookup.c
===================================================================
--- sys/fs/ext2fs/ext2_lookup.c	(revision 328478)
+++ sys/fs/ext2fs/ext2_lookup.c	(working copy)
@@ -145,7 +145,7 @@
 	off_t offset, startoffset;
 	size_t readcnt, skipcnt;
 	ssize_t startresid;
-	u_int ncookies;
+	int ncookies;
 	int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize;
 	int error;
 
@@ -152,7 +152,11 @@
 	if (uio->uio_offset < 0)
 		return (EINVAL);
 	ip = VTOI(vp);
+	if (uio->uio_resid < 0)
+		uio->uio_resid = 0;
 	if (ap->a_ncookies != NULL) {
+		if (uio->uio_resid > MAXPHYS)
+			uio->uio_resid = MAXPHYS;
 		ncookies = uio->uio_resid;
 		if (uio->uio_offset >= ip->i_size)
 			ncookies = 0;
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
--- sys/ufs/ufs/ufs_vnops.c	(revision 328478)
+++ sys/ufs/ufs/ufs_vnops.c	(working copy)
@@ -2170,7 +2170,7 @@
 	off_t offset, startoffset;
 	size_t readcnt, skipcnt;
 	ssize_t startresid;
-	u_int ncookies;
+	int ncookies;
 	int error;
 
 	if (uio->uio_offset < 0)
@@ -2178,7 +2178,11 @@
 	ip = VTOI(vp);
 	if (ip->i_effnlink == 0)
 		return (0);
+	if (uio->uio_resid < 0)
+		uio->uio_resid = 0;
 	if (ap->a_ncookies != NULL) {
+		if (uio->uio_resid > MAXPHYS)
+			uio->uio_resid = MAXPHYS;
 		ncookies = uio->uio_resid;
 		if (uio->uio_offset >= ip->i_size)
 			ncookies = 0;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to