summaryrefslogtreecommitdiffstats
path: root/mm/hugetlb.c
diff options
context:
space:
mode:
authorLinus Torvalds2019-04-11 19:49:19 +0200
committerGreg Kroah-Hartman2019-05-04 09:20:11 +0200
commitd972ebbf42ba6712460308ae57c222a0706f2af3 (patch)
treea772b21386424f5b233e40a30ac11b149e820521 /mm/hugetlb.c
parentmm: add 'try_get_page()' helper function (diff)
downloadkernel-qcow2-linux-d972ebbf42ba6712460308ae57c222a0706f2af3.tar.gz
kernel-qcow2-linux-d972ebbf42ba6712460308ae57c222a0706f2af3.tar.xz
kernel-qcow2-linux-d972ebbf42ba6712460308ae57c222a0706f2af3.zip
mm: prevent get_user_pages() from overflowing page refcount
commit 8fde12ca79aff9b5ba951fce1a2641901b8d8e64 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r--mm/hugetlb.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 9e5f66cbf711..5fb779cda972 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -4299,6 +4299,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);