[PATCH v4 08/16] fs: hugetlbfs: Fix global reservation leak in hugetlbfs_fill_super()
Ackerley Tng via B4 Relay <[email protected]> Wed, 22 Jul 2026 16:41:16 -0700
| Newsgroups | org.kernel.vger.cgroups,org.kernel.feeds.b4-sent,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.stable,org.kvack.linux-mm |
|---|---|
| Message-ID | <20260722-hugetlb-alloc-failure-fixes-v4-8-88e8b81970dc@google.com> |
From: Ackerley Tng <[email protected]> In hugetlbfs_fill_super(), if hugepage_new_subpool() succeeds in allocating a subpool (which reserves global huge pages when min_hpages != -1), but a subsequent initialization step like d_make_root() fails, the error path directly invoked kfree(sbinfo->spool). Directly freeing the subpool structure with kfree() bypasses hugepage_put_subpool() and its underlying unlock_or_release_subpool() destructor. Consequently, hugetlb_acct_memory() is never called to release the global page reservations allocated for min_hpages, permanently leaking global huge page reservations. Fix this leak by calling hugepage_put_subpool(sbinfo->spool) on the error cleanup path instead of kfree(sbinfo->spool). sbinfo->spool must be checked before dereferencing the subpool in hugepage_put_subpool(). Add a NULL guard to hugepage_put_subpool() instead of checking it in the caller to align it with other subpool helpers like hugepage_subpool_get_pages() and hugepage_subpool_put_pages() that gracefully handle NULL subpools. This allows callers to safely invoke hugepage_put_subpool() without requiring explicit NULL checks. This is also aligned with how kfree() can be called on NULL. With the NULL guard in hugepage_put_subpool(), the NULL check in the only other caller can also be removed. Fixes: 7ca02d0ae586f ("hugetlbfs: accept subpool min_size mount option and setup accordingly") Cc: [email protected] Signed-off-by: Ackerley Tng <[email protected]> --- fs/hugetlbfs/inode.c | 6 ++---- mm/hugetlb.c | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 26c0187340636..e5d86f31eba5b 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -1133,9 +1133,7 @@ static void hugetlbfs_put_super(struct super_block *sb) if (sbi) { sb->s_fs_info = NULL; - if (sbi->spool) - hugepage_put_subpool(sbi->spool); - + hugepage_put_subpool(sbi->spool); kfree(sbi); } } @@ -1423,7 +1421,7 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc) goto out_free; return 0; out_free: - kfree(sbinfo->spool); + hugepage_put_subpool(sbinfo->spool); kfree(sbinfo); return -ENOMEM; } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b759748468734..90ec015a11181 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -177,6 +177,9 @@ void hugepage_put_subpool(struct hugepage_subpool *spool) { unsigned long flags; + if (!spool) + return; + spin_lock_irqsave(&spool->lock, flags); BUG_ON(!spool->count); spool->count--; -- 2.55.0.229.g6434b31f56-goog