Skip to content

Commit 48684a6

Browse files
Naoya Horiguchitorvalds
Naoya Horiguchi
authored andcommitted
mm: pagewalk: fix misbehavior of walk_page_range for vma(VM_PFNMAP)
walk_page_range() silently skips vma having VM_PFNMAP set, which leads to undesirable behaviour at client end (who called walk_page_range). For example for pagemap_read(), when no callbacks are called against VM_PFNMAP vma, pagemap_read() may prepare pagemap data for next virtual address range at wrong index. That could confuse and/or break userspace applications. This patch avoid this misbehavior caused by vma(VM_PFNMAP) like follows: - for pagemap_read() which has its own ->pte_hole(), call the ->pte_hole() over vma(VM_PFNMAP), - for clear_refs and queue_pages which have their own ->tests_walk, just return 1 and skip vma(VM_PFNMAP). This is no problem because these are not interested in hole regions, - for other callers, just skip the vma(VM_PFNMAP) as a default behavior. Signed-off-by: Naoya Horiguchi <[email protected]> Signed-off-by: Shiraz Hashim <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 6f4576e commit 48684a6

File tree

3 files changed

+19
-8
lines changed

3 files changed

+19
-8
lines changed

fs/proc/task_mmu.c

+3
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,9 @@ static int clear_refs_test_walk(unsigned long start, unsigned long end,
806806
struct clear_refs_private *cp = walk->private;
807807
struct vm_area_struct *vma = walk->vma;
808808

809+
if (vma->vm_flags & VM_PFNMAP)
810+
return 1;
811+
809812
/*
810813
* Writing 1 to /proc/pid/clear_refs affects all pages.
811814
* Writing 2 to /proc/pid/clear_refs only affects anonymous pages.

mm/mempolicy.c

+3
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,9 @@ static int queue_pages_test_walk(unsigned long start, unsigned long end,
591591
unsigned long endvma = vma->vm_end;
592592
unsigned long flags = qp->flags;
593593

594+
if (vma->vm_flags & VM_PFNMAP)
595+
return 1;
596+
594597
if (endvma > end)
595598
endvma = end;
596599
if (vma->vm_start > start)

mm/pagewalk.c

+13-8
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
3535
do {
3636
again:
3737
next = pmd_addr_end(addr, end);
38-
if (pmd_none(*pmd)) {
38+
if (pmd_none(*pmd) || !walk->vma) {
3939
if (walk->pte_hole)
4040
err = walk->pte_hole(addr, next, walk);
4141
if (err)
@@ -165,9 +165,6 @@ static int walk_hugetlb_range(unsigned long addr, unsigned long end,
165165
* or skip it via the returned value. Return 0 if we do walk over the
166166
* current vma, and return 1 if we skip the vma. Negative values means
167167
* error, where we abort the current walk.
168-
*
169-
* Default check (only VM_PFNMAP check for now) is used when the caller
170-
* doesn't define test_walk() callback.
171168
*/
172169
static int walk_page_test(unsigned long start, unsigned long end,
173170
struct mm_walk *walk)
@@ -178,11 +175,19 @@ static int walk_page_test(unsigned long start, unsigned long end,
178175
return walk->test_walk(start, end, walk);
179176

180177
/*
181-
* Do not walk over vma(VM_PFNMAP), because we have no valid struct
182-
* page backing a VM_PFNMAP range. See also commit a9ff785e4437.
178+
* vma(VM_PFNMAP) doesn't have any valid struct pages behind VM_PFNMAP
179+
* range, so we don't walk over it as we do for normal vmas. However,
180+
* Some callers are interested in handling hole range and they don't
181+
* want to just ignore any single address range. Such users certainly
182+
* define their ->pte_hole() callbacks, so let's delegate them to handle
183+
* vma(VM_PFNMAP).
183184
*/
184-
if (vma->vm_flags & VM_PFNMAP)
185-
return 1;
185+
if (vma->vm_flags & VM_PFNMAP) {
186+
int err = 1;
187+
if (walk->pte_hole)
188+
err = walk->pte_hole(start, end, walk);
189+
return err ? err : 1;
190+
}
186191
return 0;
187192
}
188193

0 commit comments

Comments
 (0)