diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-11 19:49:19 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-04-14 19:00:04 +0200 |
commit | 8fde12ca79aff9b5ba951fce1a2641901b8d8e64 (patch) | |
tree | 23ee93c180e690ccd12257fb677fa9b40e1be53a /mm/hugetlb.c | |
parent | mm: add 'try_get_page()' helper function (diff) | |
download | linux-8fde12ca79aff9b5ba951fce1a2641901b8d8e64.tar.xz linux-8fde12ca79aff9b5ba951fce1a2641901b8d8e64.zip |
mm: prevent get_user_pages() from overflowing page refcount
If the page refcount wraps around past zero, it will be freed while
there are still four billion references to it. One of the possible
avenues for an attacker to try to make this happen is by doing direct IO
on a page multiple times. This patch makes get_user_pages() refuse to
take a new page reference if there are already more than two billion
references to the page.
Reported-by: Jann Horn <jannh@google.com>
Acked-by: Matthew Wilcox <willy@infradead.org>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to '')
-rw-r--r-- | mm/hugetlb.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 8dfdffc34a99..c220315dc533 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4298,6 +4298,19 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT; page = pte_page(huge_ptep_get(pte)); + + /* + * Instead of doing 'try_get_page()' below in the same_page + * loop, just check the count once here. + */ + if (unlikely(page_count(page) <= 0)) { + if (pages) { + spin_unlock(ptl); + remainder = 0; + err = -ENOMEM; + break; + } + } same_page: if (pages) { pages[i] = mem_map_offset(page, pfn_offset); |