summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2005-08-05 11:59:35 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2005-08-05 12:22:37 -0700
commitc7546f8f03f5a4fa612605b6be930234d6026860 (patch)
treee372cdb3856c9585587283c21b5b99a792a1a41d
parente6cb99413da42af413c11a394538ddc8b9d201e1 (diff)
downloadlinux-3.10-c7546f8f03f5a4fa612605b6be930234d6026860.tar.gz
linux-3.10-c7546f8f03f5a4fa612605b6be930234d6026860.tar.bz2
linux-3.10-c7546f8f03f5a4fa612605b6be930234d6026860.zip
[PATCH] Fix hugepage crash on failing mmap()
This patch fixes a crash in the hugepage code. unmap_hugepage_area() was assuming that (due to prefault) PTEs must exist for all the area in question. However, this may not be the case, if mmap() encounters an error before the prefault and calls unmap_region() to clean up any partial mapping. Depending on the hugepage configuration, this crash can be triggered by an unpriveleged user. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Cc: William Lee Irwin III <wli@holomorphy.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--mm/hugetlb.c11
1 files changed, 10 insertions, 1 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index fbd1111ea11..6bf720bc662 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -301,6 +301,7 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
{
struct mm_struct *mm = vma->vm_mm;
unsigned long address;
+ pte_t *ptep;
pte_t pte;
struct page *page;
@@ -309,9 +310,17 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
BUG_ON(end & ~HPAGE_MASK);
for (address = start; address < end; address += HPAGE_SIZE) {
- pte = huge_ptep_get_and_clear(mm, address, huge_pte_offset(mm, address));
+ ptep = huge_pte_offset(mm, address);
+ if (! ptep)
+ /* This can happen on truncate, or if an
+ * mmap() is aborted due to an error before
+ * the prefault */
+ continue;
+
+ pte = huge_ptep_get_and_clear(mm, address, ptep);
if (pte_none(pte))
continue;
+
page = pte_page(pte);
put_page(page);
}