Skip to content

Commit

Permalink
mm/hugetlb.c: __get_user_pages ignores certain follow_hugetlb_page er…
Browse files Browse the repository at this point in the history
…rors

Commit 9a291a7 ("mm/hugetlb: report -EHWPOISON not -EFAULT when
FOLL_HWPOISON is specified") causes __get_user_pages to ignore certain
errors from follow_hugetlb_page.  After such error, __get_user_pages
subsequently calls faultin_page on the same VMA and start address that
follow_hugetlb_page failed on instead of returning the error immediately
as it should.

In follow_hugetlb_page, when hugetlb_fault returns a value covered under
VM_FAULT_ERROR, follow_hugetlb_page returns it without setting nr_pages
to 0 as __get_user_pages expects in this case, which causes the
following to happen in __get_user_pages: the "while (nr_pages)" check
succeeds, we skip the "if (!vma..." check because we got a VMA the last
time around, we find no page with follow_page_mask, and we call
faultin_page, which calls hugetlb_fault for the second time.

This issue also slightly changes how __get_user_pages works.  Before, it
only returned error if it had made no progress (i = 0).  But now,
follow_hugetlb_page can clobber "i" with an error code since its new
return path doesn't check for progress.  So if "i" is nonzero before a
failing call to follow_hugetlb_page, that indication of progress is lost
and __get_user_pages can return error even if some pages were
successfully pinned.

To fix this, change follow_hugetlb_page so that it updates nr_pages,
allowing __get_user_pages to fail immediately and restoring the "error
only if no progress" behavior to __get_user_pages.

Tested that __get_user_pages returns when expected on error from
hugetlb_fault in follow_hugetlb_page.

Fixes: 9a291a7 ("mm/hugetlb: report -EHWPOISON not -EFAULT when FOLL_HWPOISON is specified")
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Daniel Jordan <[email protected]>
Acked-by: Punit Agrawal <[email protected]>
Cc: Andrea Arcangeli <[email protected]>
Cc: "Aneesh Kumar K.V" <[email protected]>
Cc: Gerald Schaefer <[email protected]>
Cc: James Morse <[email protected]>
Cc: "Kirill A. Shutemov" <[email protected]>
Cc: Michal Hocko <[email protected]>
Cc: Mike Kravetz <[email protected]>
Cc: Naoya Horiguchi <[email protected]>
Cc: zhong jiang <[email protected]>
Cc: <[email protected]>	[4.12.x]
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
danieljordan10 authored and torvalds committed Aug 2, 2017
1 parent 4d3f5d0 commit 2be7cfe
Showing 1 changed file with 3 additions and 6 deletions.
9 changes: 3 additions & 6 deletions mm/hugetlb.c
Original file line number Diff line number Diff line change
Expand Up @@ -4078,6 +4078,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long vaddr = *position;
unsigned long remainder = *nr_pages;
struct hstate *h = hstate_vma(vma);
int err = -EFAULT;

while (vaddr < vma->vm_end && remainder) {
pte_t *pte;
Expand Down Expand Up @@ -4154,11 +4155,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
}
ret = hugetlb_fault(mm, vma, vaddr, fault_flags);
if (ret & VM_FAULT_ERROR) {
int err = vm_fault_to_errno(ret, flags);

if (err)
return err;

err = vm_fault_to_errno(ret, flags);
remainder = 0;
break;
}
Expand Down Expand Up @@ -4213,7 +4210,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
*/
*position = vaddr;

return i ? i : -EFAULT;
return i ? i : err;
}

#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE
Expand Down

0 comments on commit 2be7cfe

Please sign in to comment.