Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm/mlock.c: prevent walking off the end of a pagetable in no-pmd configuration

The function __munlock_pagevec_fill() introduced in commit 7a8010cd3627
("mm: munlock: manual pte walk in fast path instead of
follow_page_mask()") uses pmd_addr_end() for restricting its operation
within current page table.

This is insufficient on architectures/configurations where pmd is folded
and pmd_addr_end() just returns the end of the full range to be walked.
In this case, it allows pte++ to walk off the end of a page table
resulting in unpredictable behaviour.

This patch fixes the function by using pgd_addr_end() and pud_addr_end()
before pmd_addr_end(), which will yield correct page table boundary on
all configurations. This is similar to what existing page walkers do
when walking each level of the page table.

Additionaly, the patch clarifies a comment for get_locked_pte() call in the
function.

Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Reviewed-by: Bob Liu <bob.liu@oracle.com>
Cc: Jörn Engel <joern@logfs.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michel Lespinasse <walken@google.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Vlastimil Babka and committed by
Linus Torvalds
eadb41ae 117aad1e

+6 -2
+6 -2
mm/mlock.c
··· 379 379 380 380 /* 381 381 * Initialize pte walk starting at the already pinned page where we 382 - * are sure that there is a pte. 382 + * are sure that there is a pte, as it was pinned under the same 383 + * mmap_sem write op. 383 384 */ 384 385 pte = get_locked_pte(vma->vm_mm, start, &ptl); 385 - end = min(end, pmd_addr_end(start, end)); 386 + /* Make sure we do not cross the page table boundary */ 387 + end = pgd_addr_end(start, end); 388 + end = pud_addr_end(start, end); 389 + end = pmd_addr_end(start, end); 386 390 387 391 /* The page next to the pinned page is the first we will try to get */ 388 392 start += PAGE_SIZE;