Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758539Ab2EUUad (ORCPT ); Mon, 21 May 2012 16:30:33 -0400 Received: from e9.ny.us.ibm.com ([32.97.182.139]:60876 "EHLO e9.ny.us.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758520Ab2EUUab (ORCPT ); Mon, 21 May 2012 16:30:31 -0400 Subject: [RFC][PATCH 1/2] hugetlb: fix resv_map leak in error path To: linux-kernel@vger.kernel.org Cc: linux-mm@kvack.org, Dave Hansen From: Dave Hansen Date: Mon, 21 May 2012 13:30:22 -0700 Message-Id: <20120521203022.F7FCE507@kernel> X-Content-Scanned: Fidelis XPS MAILER x-cbid: 12052120-7182-0000-0000-00000190D701 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 3255 Lines: 104 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. This hasn't been extensively tested. Pretty much compile and boot tested along with Christoph's test case: http://marc.info/?l=linux-mm&m=133728900729735 Signed-off-by: Dave Hansen Acked-by: Mel Gorman ecked-by: KOSAKI Motohiro Reported/tested-by: Christoph Lameter --- linux-2.6.git-dave/mm/hugetlb.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff -puN mm/hugetlb.c~hugetlb-fix-leak mm/hugetlb.c --- linux-2.6.git/mm/hugetlb.c~hugetlb-fix-leak 2012-05-21 13:24:38.369857759 -0700 +++ linux-2.6.git-dave/mm/hugetlb.c 2012-05-21 13:24:38.377857849 -0700 @@ -2157,6 +2157,15 @@ static void hugetlb_vm_op_open(struct vm 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 v 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); @@ -2990,12 +2999,16 @@ int hugetlb_reserve_pages(struct 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. @@ -3004,7 +3017,7 @@ int hugetlb_reserve_pages(struct inode * ret = hugetlb_acct_memory(h, chg); if (ret < 0) { hugepage_subpool_put_pages(spool, chg); - return ret; + goto out_err; } /* @@ -3021,6 +3034,9 @@ int hugetlb_reserve_pages(struct 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) diff -puN Documentation/stable_kernel_rules.txt~hugetlb-fix-leak Documentation/stable_kernel_rules.txt _ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/