Skip to content

Commit c50ac05

Browse files
Dave Hansentorvalds
Dave Hansen
authored andcommitted
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 <[email protected]> Acked-by: Mel Gorman <[email protected]> Acked-by: KOSAKI Motohiro <[email protected]> Reported-by: Christoph Lameter <[email protected]> Tested-by: Christoph Lameter <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: <[email protected]> [2.6.32+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5c2b8a1 commit c50ac05

File tree

1 file changed

+22
-6
lines changed

1 file changed

+22
-6
lines changed

mm/hugetlb.c

+22-6
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,15 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma)
21572157
kref_get(&reservations->refs);
21582158
}
21592159

2160+
static void resv_map_put(struct vm_area_struct *vma)
2161+
{
2162+
struct resv_map *reservations = vma_resv_map(vma);
2163+
2164+
if (!reservations)
2165+
return;
2166+
kref_put(&reservations->refs, resv_map_release);
2167+
}
2168+
21602169
static void hugetlb_vm_op_close(struct vm_area_struct *vma)
21612170
{
21622171
struct hstate *h = hstate_vma(vma);
@@ -2173,7 +2182,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
21732182
reserve = (end - start) -
21742183
region_count(&reservations->regions, start, end);
21752184

2176-
kref_put(&reservations->refs, resv_map_release);
2185+
resv_map_put(vma);
21772186

21782187
if (reserve) {
21792188
hugetlb_acct_memory(h, -reserve);
@@ -2991,12 +3000,16 @@ int hugetlb_reserve_pages(struct inode *inode,
29913000
set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
29923001
}
29933002

2994-
if (chg < 0)
2995-
return chg;
3003+
if (chg < 0) {
3004+
ret = chg;
3005+
goto out_err;
3006+
}
29963007

29973008
/* There must be enough pages in the subpool for the mapping */
2998-
if (hugepage_subpool_get_pages(spool, chg))
2999-
return -ENOSPC;
3009+
if (hugepage_subpool_get_pages(spool, chg)) {
3010+
ret = -ENOSPC;
3011+
goto out_err;
3012+
}
30003013

30013014
/*
30023015
* Check enough hugepages are available for the reservation.
@@ -3005,7 +3018,7 @@ int hugetlb_reserve_pages(struct inode *inode,
30053018
ret = hugetlb_acct_memory(h, chg);
30063019
if (ret < 0) {
30073020
hugepage_subpool_put_pages(spool, chg);
3008-
return ret;
3021+
goto out_err;
30093022
}
30103023

30113024
/*
@@ -3022,6 +3035,9 @@ int hugetlb_reserve_pages(struct inode *inode,
30223035
if (!vma || vma->vm_flags & VM_MAYSHARE)
30233036
region_add(&inode->i_mapping->private_list, from, to);
30243037
return 0;
3038+
out_err:
3039+
resv_map_put(vma);
3040+
return ret;
30253041
}
30263042

30273043
void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)

0 commit comments

Comments
 (0)