Re: [PATCH v3] mm/hugetlb: Fix null nodemask in alloc_fresh_hugetlb_folio
From: Andrew Morton
Date: Sun Jul 05 2026 - 15:04:20 EST
On Sun, 5 Jul 2026 17:51:19 +0000 Sourav Panda <souravpanda@xxxxxxxxxx> wrote:
> alloc_buddy_hugetlb_folio_with_mpol() can pass a NULL nodemask to
> alloc_fresh_hugetlb_folio() as a fallback to allocate from all
> nodes. If order is gigantic, alloc_fresh_hugetlb_folio() propagates
> the NULL nodemask down to hugetlb_cma_alloc_frozen_folio() which blindly
> dereferences it in for_each_node_mask(), leading to a null pointer
> dereference.
>
> Similarly, if the CMA allocation fails, the fallback
> alloc_contig_frozen_pages() is also called with a NULL nodemask,
> which may cause issues.
>
> Fix this by explicitly checking if nodemask is NULL in
> alloc_fresh_hugetlb_folio() and defaulting to
> cpuset_current_mems_allowed. This ensures that both the CMA and
> contiguous allocators receive a valid nodemask safely using a seqcount
> loop to prevent torn reads.
>
> From a userspace perspective, this bug allows an unprivileged user to
> crash the kernel (trigger a panic) by requesting a gigantic hugepage
> allocation with MPOL_PREFERRED_MANY on a system where CMA is only
> configured on a subset of NUMA nodes.
ow.
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -1864,6 +1864,18 @@ static struct folio *alloc_fresh_hugetlb_folio(struct hstate *h,
> gfp_t gfp_mask, int nid, nodemask_t *nmask)
> {
> struct folio *folio;
> + nodemask_t local_node_mask;
> +
> + if (!nmask) {
> + unsigned int cpuset_mems_cookie;
> +
> + do {
> + cpuset_mems_cookie = read_mems_allowed_begin();
> + local_node_mask = cpuset_current_mems_allowed;
> + } while (read_mems_allowed_retry(cpuset_mems_cookie));
> +
> + nmask = &local_node_mask;
> + }
For my edification, is there anything which prevents
cpuset_current_mems_allowed from changing after it has been read? So
we end up with a folio on an inappropriate node? Or does that not
really matter much.
cpuset_current_mems_allowed is a nasty thing. It looks like a simple
global variable, but appearances can be deceptive:
#ifdef ...
#define cpuset_current_mems_allowed (current->mems_allowed)
#else
#define cpuset_current_mems_allowed (node_states[N_MEMORY])
#endif
Thanks, I'll queue this for a bit of testing and shall await reviewer
input.