aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-04-11 13:49:19 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-04-14 13:00:04 -0400
commit8fde12ca79aff9b5ba951fce1a2641901b8d8e64 (patch)
tree23ee93c180e690ccd12257fb677fa9b40e1be53a /mm
parent88b1a17dfc3ed7728316478fae0f5ad508f50397 (diff)
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 'mm')
-rw-r--r--mm/gup.c48
-rw-r--r--mm/hugetlb.c13
2 files changed, 49 insertions, 12 deletions
diff --git a/mm/gup.c b/mm/gup.c
index 75029649baca..81e0bdefa2cc 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -157,8 +157,12 @@ retry:
157 goto retry; 157 goto retry;
158 } 158 }
159 159
160 if (flags & FOLL_GET) 160 if (flags & FOLL_GET) {
161 get_page(page); 161 if (unlikely(!try_get_page(page))) {
162 page = ERR_PTR(-ENOMEM);
163 goto out;
164 }
165 }
162 if (flags & FOLL_TOUCH) { 166 if (flags & FOLL_TOUCH) {
163 if ((flags & FOLL_WRITE) && 167 if ((flags & FOLL_WRITE) &&
164 !pte_dirty(pte) && !PageDirty(page)) 168 !pte_dirty(pte) && !PageDirty(page))
@@ -295,7 +299,10 @@ retry_locked:
295 if (pmd_trans_unstable(pmd)) 299 if (pmd_trans_unstable(pmd))
296 ret = -EBUSY; 300 ret = -EBUSY;
297 } else { 301 } else {
298 get_page(page); 302 if (unlikely(!try_get_page(page))) {
303 spin_unlock(ptl);
304 return ERR_PTR(-ENOMEM);
305 }
299 spin_unlock(ptl); 306 spin_unlock(ptl);
300 lock_page(page); 307 lock_page(page);
301 ret = split_huge_page(page); 308 ret = split_huge_page(page);
@@ -497,7 +504,10 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address,
497 if (is_device_public_page(*page)) 504 if (is_device_public_page(*page))
498 goto unmap; 505 goto unmap;
499 } 506 }
500 get_page(*page); 507 if (unlikely(!try_get_page(*page))) {
508 ret = -ENOMEM;
509 goto unmap;
510 }
501out: 511out:
502 ret = 0; 512 ret = 0;
503unmap: 513unmap:
@@ -1393,6 +1403,20 @@ static void undo_dev_pagemap(int *nr, int nr_start, struct page **pages)
1393 } 1403 }
1394} 1404}
1395 1405
1406/*
1407 * Return the compund head page with ref appropriately incremented,
1408 * or NULL if that failed.
1409 */
1410static inline struct page *try_get_compound_head(struct page *page, int refs)
1411{
1412 struct page *head = compound_head(page);
1413 if (WARN_ON_ONCE(page_ref_count(head) < 0))
1414 return NULL;
1415 if (unlikely(!page_cache_add_speculative(head, refs)))
1416 return NULL;
1417 return head;
1418}
1419
1396#ifdef CONFIG_ARCH_HAS_PTE_SPECIAL 1420#ifdef CONFIG_ARCH_HAS_PTE_SPECIAL
1397static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end, 1421static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
1398 int write, struct page **pages, int *nr) 1422 int write, struct page **pages, int *nr)
@@ -1427,9 +1451,9 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
1427 1451
1428 VM_BUG_ON(!pfn_valid(pte_pfn(pte))); 1452 VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
1429 page = pte_page(pte); 1453 page = pte_page(pte);
1430 head = compound_head(page);
1431 1454
1432 if (!page_cache_get_speculative(head)) 1455 head = try_get_compound_head(page, 1);
1456 if (!head)
1433 goto pte_unmap; 1457 goto pte_unmap;
1434 1458
1435 if (unlikely(pte_val(pte) != pte_val(*ptep))) { 1459 if (unlikely(pte_val(pte) != pte_val(*ptep))) {
@@ -1568,8 +1592,8 @@ static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
1568 refs++; 1592 refs++;
1569 } while (addr += PAGE_SIZE, addr != end); 1593 } while (addr += PAGE_SIZE, addr != end);
1570 1594
1571 head = compound_head(pmd_page(orig)); 1595 head = try_get_compound_head(pmd_page(orig), refs);
1572 if (!page_cache_add_speculative(head, refs)) { 1596 if (!head) {
1573 *nr -= refs; 1597 *nr -= refs;
1574 return 0; 1598 return 0;
1575 } 1599 }
@@ -1606,8 +1630,8 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
1606 refs++; 1630 refs++;
1607 } while (addr += PAGE_SIZE, addr != end); 1631 } while (addr += PAGE_SIZE, addr != end);
1608 1632
1609 head = compound_head(pud_page(orig)); 1633 head = try_get_compound_head(pud_page(orig), refs);
1610 if (!page_cache_add_speculative(head, refs)) { 1634 if (!head) {
1611 *nr -= refs; 1635 *nr -= refs;
1612 return 0; 1636 return 0;
1613 } 1637 }
@@ -1643,8 +1667,8 @@ static int gup_huge_pgd(pgd_t orig, pgd_t *pgdp, unsigned long addr,
1643 refs++; 1667 refs++;
1644 } while (addr += PAGE_SIZE, addr != end); 1668 } while (addr += PAGE_SIZE, addr != end);
1645 1669
1646 head = compound_head(pgd_page(orig)); 1670 head = try_get_compound_head(pgd_page(orig), refs);
1647 if (!page_cache_add_speculative(head, refs)) { 1671 if (!head) {
1648 *nr -= refs; 1672 *nr -= refs;
1649 return 0; 1673 return 0;
1650 } 1674 }
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,
4298 4298
4299 pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT; 4299 pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT;
4300 page = pte_page(huge_ptep_get(pte)); 4300 page = pte_page(huge_ptep_get(pte));
4301
4302 /*
4303 * Instead of doing 'try_get_page()' below in the same_page
4304 * loop, just check the count once here.
4305 */
4306 if (unlikely(page_count(page) <= 0)) {
4307 if (pages) {
4308 spin_unlock(ptl);
4309 remainder = 0;
4310 err = -ENOMEM;
4311 break;
4312 }
4313 }
4301same_page: 4314same_page:
4302 if (pages) { 4315 if (pages) {
4303 pages[i] = mem_map_offset(page, pfn_offset); 4316 pages[i] = mem_map_offset(page, pfn_offset);