mm: tighten fault_in_pages_writeable()
authorEric Dumazet <edumazet@google.com>
Fri, 20 May 2016 23:57:53 +0000 (16:57 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 21 May 2016 00:58:30 +0000 (17:58 -0700)
copy_page_to_iter_iovec() is currently the only user of
fault_in_pages_writeable(), and it definitely can use fragments from
high order pages.

Make sure fault_in_pages_writeable() is only touching two adjacent pages
at most, as claimed.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/pagemap.h

index fe1513f..9735410 100644 (file)
@@ -518,33 +518,27 @@ void page_endio(struct page *page, int rw, int err);
 extern void add_page_wait_queue(struct page *page, wait_queue_t *waiter);
 
 /*
- * Fault a userspace page into pagetables.  Return non-zero on a fault.
- *
- * This assumes that two userspace pages are always sufficient.
+ * Fault one or two userspace pages into pagetables.
+ * Return -EINVAL if more than two pages would be needed.
+ * Return non-zero on a fault.
  */
 static inline int fault_in_pages_writeable(char __user *uaddr, int size)
 {
-       int ret;
+       int span, ret;
 
        if (unlikely(size == 0))
                return 0;
 
+       span = offset_in_page(uaddr) + size;
+       if (span > 2 * PAGE_SIZE)
+               return -EINVAL;
        /*
         * Writing zeroes into userspace here is OK, because we know that if
         * the zero gets there, we'll be overwriting it.
         */
        ret = __put_user(0, uaddr);
-       if (ret == 0) {
-               char __user *end = uaddr + size - 1;
-
-               /*
-                * If the page was already mapped, this will get a cache miss
-                * for sure, so try to avoid doing it.
-                */
-               if (((unsigned long)uaddr & PAGE_MASK) !=
-                               ((unsigned long)end & PAGE_MASK))
-                       ret = __put_user(0, end);
-       }
+       if (ret == 0 && span > PAGE_SIZE)
+               ret = __put_user(0, uaddr + size - 1);
        return ret;
 }