]> granicus.if.org Git - zfs/commitdiff
Fix uio_prefaultpages for 0 length iovec
authorChunwei Chen <david.chen@osnexus.com>
Tue, 8 Dec 2015 20:26:18 +0000 (12:26 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 16 Dec 2015 00:19:55 +0000 (16:19 -0800)
Userspace can freely pass in whatever iovec it feels like, and it's perfectly
legal to pass an iovec which contains a zero length segment. In the current
implementation, uio_prefaultpages would touch an out of bound byte in the
"last byte" logic. While this probably wouldn't cause any critical error, we
would like uio_prefaultpages to be able to continue gracefully.

Signed-off-by: Chunwei Chen <david.chen@osnexus.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #4078

module/zcommon/zfs_uio.c

index f78db68e4ea640eafce35504b783b62f622855b9..9ec3002a29e4128998cb825eb3680cef9dc484d6 100644 (file)
@@ -164,7 +164,7 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
        caddr_t p;
        uint8_t tmp;
        int iovcnt;
-       size_t skip = uio->uio_skip;
+       size_t skip;
 
        /* no need to fault in kernel pages */
        switch (uio->uio_segflg) {
@@ -180,9 +180,13 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
 
        iov = uio->uio_iov;
        iovcnt = uio->uio_iovcnt;
+       skip = uio->uio_skip;
 
-       while ((n > 0) && (iovcnt > 0)) {
+       for (; n > 0 && iovcnt > 0; iov++, iovcnt--, skip = 0) {
                cnt = MIN(iov->iov_len - skip, n);
+               /* empty iov */
+               if (cnt == 0)
+                       continue;
                n -= cnt;
                /*
                 * touch each page in this segment.
@@ -201,9 +205,6 @@ uio_prefaultpages(ssize_t n, struct uio *uio)
                p--;
                if (fuword8((uint8_t *) p, &tmp))
                        return;
-               iov++;
-               iovcnt--;
-               skip = 0;
        }
 }
 EXPORT_SYMBOL(uio_prefaultpages);