Fix 'get_user_pages_fast()' with non-page-aligned start address
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Jul 2008 00:54:21 +0000 (17:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Jul 2008 00:54:21 +0000 (17:54 -0700)
Alexey Dobriyan reported trouble with LTP with the new fast-gup code,
and Johannes Weiner debugged it to non-page-aligned addresses, where the
new get_user_pages_fast() code would do all the wrong things, including
just traversing past the end of the requested area due to 'addr' never
matching 'end' exactly.

This is not a pretty fix, and we may actually want to move the alignment
into generic code, leaving just the core code per-arch, but Alexey
verified that the vmsplice01 LTP test doesn't crash with this.

Reported-and-tested-by: Alexey Dobriyan <adobriyan@gmail.com>
Debugged-by: Johannes Weiner <hannes@saeurebad.de>
Cc: Nick Piggin <npiggin@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/x86/mm/gup.c

index 3085f25b4355de827fee53d8851180a844777517..007bb06c750477b60c158f667f5d0563c2d77433 100644 (file)
@@ -223,14 +223,17 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
                        struct page **pages)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long end = start + (nr_pages << PAGE_SHIFT);
-       unsigned long addr = start;
+       unsigned long addr, len, end;
        unsigned long next;
        pgd_t *pgdp;
        int nr = 0;
 
+       start &= PAGE_MASK;
+       addr = start;
+       len = (unsigned long) nr_pages << PAGE_SHIFT;
+       end = start + len;
        if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ,
-                                       start, nr_pages*PAGE_SIZE)))
+                                       start, len)))
                goto slow_irqon;
 
        /*