d0d0771165
This fixes an oops that was trivially triggered after the original commit went in. Props to Dave J.
177 lines
5.4 KiB
Diff
177 lines
5.4 KiB
Diff
From c50ac050811d6485616a193eb0f37bfbd191cc89 Mon Sep 17 00:00:00 2001
|
|
From: Dave Hansen <dave@linux.vnet.ibm.com>
|
|
Date: Tue, 29 May 2012 15:06:46 -0700
|
|
Subject: [PATCH] hugetlb: fix resv_map leak in error path
|
|
|
|
When called for anonymous (non-shared) mappings, hugetlb_reserve_pages()
|
|
does a resv_map_alloc(). It depends on code in hugetlbfs's
|
|
vm_ops->close() to release that allocation.
|
|
|
|
However, in the mmap() failure path, we do a plain unmap_region() without
|
|
the remove_vma() which actually calls vm_ops->close().
|
|
|
|
This is a decent fix. This leak could get reintroduced if new code (say,
|
|
after hugetlb_reserve_pages() in hugetlbfs_file_mmap()) decides to return
|
|
an error. But, I think it would have to unroll the reservation anyway.
|
|
|
|
Christoph's test case:
|
|
|
|
http://marc.info/?l=linux-mm&m=133728900729735
|
|
|
|
This patch applies to 3.4 and later. A version for earlier kernels is at
|
|
https://lkml.org/lkml/2012/5/22/418.
|
|
|
|
Signed-off-by: Dave Hansen <dave@linux.vnet.ibm.com>
|
|
Acked-by: Mel Gorman <mel@csn.ul.ie>
|
|
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
|
|
Reported-by: Christoph Lameter <cl@linux.com>
|
|
Tested-by: Christoph Lameter <cl@linux.com>
|
|
Cc: Andrea Arcangeli <aarcange@redhat.com>
|
|
Cc: <stable@vger.kernel.org> [2.6.32+]
|
|
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
|
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
---
|
|
mm/hugetlb.c | 28 ++++++++++++++++++++++------
|
|
1 files changed, 22 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
|
|
index 41a647d..285a81e 100644
|
|
--- a/mm/hugetlb.c
|
|
+++ b/mm/hugetlb.c
|
|
@@ -2157,6 +2157,15 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma)
|
|
kref_get(&reservations->refs);
|
|
}
|
|
|
|
+static void resv_map_put(struct vm_area_struct *vma)
|
|
+{
|
|
+ struct resv_map *reservations = vma_resv_map(vma);
|
|
+
|
|
+ if (!reservations)
|
|
+ return;
|
|
+ kref_put(&reservations->refs, resv_map_release);
|
|
+}
|
|
+
|
|
static void hugetlb_vm_op_close(struct vm_area_struct *vma)
|
|
{
|
|
struct hstate *h = hstate_vma(vma);
|
|
@@ -2173,7 +2182,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
|
|
reserve = (end - start) -
|
|
region_count(&reservations->regions, start, end);
|
|
|
|
- kref_put(&reservations->refs, resv_map_release);
|
|
+ resv_map_put(vma);
|
|
|
|
if (reserve) {
|
|
hugetlb_acct_memory(h, -reserve);
|
|
@@ -2991,12 +3000,16 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
|
|
}
|
|
|
|
- if (chg < 0)
|
|
- return chg;
|
|
+ if (chg < 0) {
|
|
+ ret = chg;
|
|
+ goto out_err;
|
|
+ }
|
|
|
|
/* There must be enough pages in the subpool for the mapping */
|
|
- if (hugepage_subpool_get_pages(spool, chg))
|
|
- return -ENOSPC;
|
|
+ if (hugepage_subpool_get_pages(spool, chg)) {
|
|
+ ret = -ENOSPC;
|
|
+ goto out_err;
|
|
+ }
|
|
|
|
/*
|
|
* Check enough hugepages are available for the reservation.
|
|
@@ -3005,7 +3018,7 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
ret = hugetlb_acct_memory(h, chg);
|
|
if (ret < 0) {
|
|
hugepage_subpool_put_pages(spool, chg);
|
|
- return ret;
|
|
+ goto out_err;
|
|
}
|
|
|
|
/*
|
|
@@ -3022,6 +3035,9 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
if (!vma || vma->vm_flags & VM_MAYSHARE)
|
|
region_add(&inode->i_mapping->private_list, from, to);
|
|
return 0;
|
|
+out_err:
|
|
+ resv_map_put(vma);
|
|
+ return ret;
|
|
}
|
|
|
|
void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
|
|
--
|
|
1.7.7.6
|
|
|
|
From 4523e1458566a0e8ecfaff90f380dd23acc44d27 Mon Sep 17 00:00:00 2001
|
|
From: Dave Hansen <dave@linux.vnet.ibm.com>
|
|
Date: Wed, 30 May 2012 07:51:07 -0700
|
|
Subject: [PATCH] mm: fix vma_resv_map() NULL pointer
|
|
|
|
hugetlb_reserve_pages() can be used for either normal file-backed
|
|
hugetlbfs mappings, or MAP_HUGETLB. In the MAP_HUGETLB, semi-anonymous
|
|
mode, there is not a VMA around. The new call to resv_map_put() assumed
|
|
that there was, and resulted in a NULL pointer dereference:
|
|
|
|
BUG: unable to handle kernel NULL pointer dereference at 0000000000000030
|
|
IP: vma_resv_map+0x9/0x30
|
|
PGD 141453067 PUD 1421e1067 PMD 0
|
|
Oops: 0000 [#1] PREEMPT SMP
|
|
...
|
|
Pid: 14006, comm: trinity-child6 Not tainted 3.4.0+ #36
|
|
RIP: vma_resv_map+0x9/0x30
|
|
...
|
|
Process trinity-child6 (pid: 14006, threadinfo ffff8801414e0000, task ffff8801414f26b0)
|
|
Call Trace:
|
|
resv_map_put+0xe/0x40
|
|
hugetlb_reserve_pages+0xa6/0x1d0
|
|
hugetlb_file_setup+0x102/0x2c0
|
|
newseg+0x115/0x360
|
|
ipcget+0x1ce/0x310
|
|
sys_shmget+0x5a/0x60
|
|
system_call_fastpath+0x16/0x1b
|
|
|
|
This was reported by Dave Jones, but was reproducible with the
|
|
libhugetlbfs test cases, so shame on me for not running them in the
|
|
first place.
|
|
|
|
With this, the oops is gone, and the output of libhugetlbfs's
|
|
run_tests.py is identical to plain 3.4 again.
|
|
|
|
[ Marked for stable, since this was introduced by commit c50ac050811d
|
|
("hugetlb: fix resv_map leak in error path") which was also marked for
|
|
stable ]
|
|
|
|
Reported-by: Dave Jones <davej@redhat.com>
|
|
Cc: Mel Gorman <mel@csn.ul.ie>
|
|
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
|
|
Cc: Christoph Lameter <cl@linux.com>
|
|
Cc: Andrea Arcangeli <aarcange@redhat.com>
|
|
Cc: Andrew Morton <akpm@linux-foundation.org>
|
|
Cc: <stable@vger.kernel.org> [2.6.32+]
|
|
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
---
|
|
mm/hugetlb.c | 3 ++-
|
|
1 files changed, 2 insertions(+), 1 deletions(-)
|
|
|
|
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
|
|
index 285a81e..e198831 100644
|
|
--- a/mm/hugetlb.c
|
|
+++ b/mm/hugetlb.c
|
|
@@ -3036,7 +3036,8 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|
region_add(&inode->i_mapping->private_list, from, to);
|
|
return 0;
|
|
out_err:
|
|
- resv_map_put(vma);
|
|
+ if (vma)
|
|
+ resv_map_put(vma);
|
|
return ret;
|
|
}
|
|
|
|
--
|
|
1.7.7.6
|
|
|