Re: [PATCH] mm: don't allow empty relative nodemask in mpol_relative_nodemask()

From: Joshua Hahn

Date: Fri May 29 2026 - 15:03:54 EST


On Fri, 29 May 2026 13:47:12 -0400 Yury Norov <ynorov@xxxxxxxxxx> wrote:

> On Fri, May 29, 2026 at 08:26:15AM -0700, Joshua Hahn wrote:
> > On Thu, 28 May 2026 12:41:33 -0700 Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> wrote:
> >
> > > On Thu, 28 May 2026 15:03:37 -0400 Yury Norov <ynorov@xxxxxxxxxx> wrote:
> > >
> > > > Reassigning nodes relative an empty user-provided nodemask is useless,
> > > > and triggers divide-by-zero in the function.
> > > >
> > > > Reported-by: Farhad Alemi <farhad.alemi@xxxxxxxxxxxx>
> > > > Link: https://lore.kernel.org/all/CA+0ovCgxbZkXa+OU8w3s84R3KNPNxxRfmsNR-udh+afQBbGNmw@xxxxxxxxxxxxxx/
> > >
> > > Thanks both.
> > >
> > > It looks like this is very old code, so we'll be wanting a cc:stable in
> > > this.
> > >
> > > > --- a/mm/mempolicy.c
> > > > +++ b/mm/mempolicy.c
> > > > @@ -370,8 +370,13 @@ static inline int mpol_store_user_nodemask(const struct mempolicy *pol)
> > > > static void mpol_relative_nodemask(nodemask_t *ret, const nodemask_t *orig,
> > > > const nodemask_t *rel)
> > > > {
> > > > + unsigned int w = nodes_weight(*rel);
> > > > nodemask_t tmp;
> > > > - nodes_fold(tmp, *orig, nodes_weight(*rel));
> > > > +
> > > > + if (w == 0)
> > > > + return -EINVAL;
> > > > +
> > > > + nodes_fold(tmp, *orig, w);
> > > > nodes_onto(*ret, tmp, *rel);
> > > > }
> > >
> > > I suspect we should address this at the mpol level - it should never
> > > have got that far. Hopefully the mempolicy maintainers can have a
> > > think.
> >
> > Hello Andrew, hello Yury,
> >
> > I agree with Andrew here.
> > mpol_relative_nodemask is called from two places, the first being
> > mpol_rebind_nodemask which is the calling function seen in the bug report as
> > well.
> >
> > The other place is mpol_set_nodemask, which has a helpful comment that notes:
> > "mpol_set_nodemask is called after mpol_new() [...snip...] mpol_new() has
> > already validated the nodes parameter with respect to the policy mode and
> > flags".
> >
> > So it seems like we are missing the big if-else if-else if block from mpol_new
> > in other places that should in fact have it, like mpol_rebind_nodemask.
> >
> > The approach proposed here of just checking whether the node weight is 0
> > won't work for a few cases, namely for MPOL_DEFAULT and MPOL_PREFERRED where
> > empty nodemasks are actually allowed. So what should really be done here is to
> > do the full policy-nodemask checking section in mpol_new and call that from
> > mpol_set_nodemask as well.
> >
> > Thank you for taking a shot at fixing the bug report, please let me know what
> > you think! Have a great day : -)
>
> Hi Joshua.
>
> Indeed, quick and dirty shot.
>
> The problem is that nodes_fold() can't work with the sz == 0. In
> other words, folding to a 0-bit bitmap is an error. We don't check
> that on bitmaps level because it's an internal helper, and it's a
> caller's responsibility to validate the parameters.
>
> nodes_onto(), or more specifically bitmap_onto(), is a different
> story. In case of empty relmap, the function actually clears all the
> bits in dst and returns.

I see, thank you for helping me understand. Yeah, we probably don't want
an empty nodemask here regardless of policy, as long as MPOL_F_RELATIVE_NODES
is set.

> I see 2 options to move this forward.
>
> 1. Simply disallow empty relmap in mpol_relative_nodemask(). There's
> no valid cases for it, AFAIK, so the nodes_fold() limitation looks
> reasonable. We can consider it as a new policy.
>
> We've got 2 users for mpol_relative_nodemask(). In mpol_set_nodemask()
> we can simply propagate the error; and in mpol_rebind_nodemask() we
> can throw a warning and do nothing.

I think we should never be able to reach mpol_set_nodemask with an empty
nodemask if MPOL_F_RELATIVE_NODES is set. Not sure if we need to be extra
defensive here.

For mpol_rebind_nodemask I think we should actually do some more checks,
I think we should do it in mpol_rebind_policy since it gives us an opportunity
to catch other sources of failure too, like calling mpol_rebind_preferred
with an empty nodemask as well (which shouldn't be allowed for MPOL_F_{
RELATIVE, STATIC}_NODES) as far as I can tell from the checks in mpol_new.

Setting empty nodemask for mpol_rebind_preferred won't throw a div0 error
like for mpol_rebind_nodemask but we can at least throw a warning like you
suggested.

Does that make sense? This is your fix and if you would prefer to address only
the div0 case, that makes sense too, since the empty nodemask for preferred
is more of a semantic incorrectness and will not cause panics.
Entirely up to you! : -)

> 2. Follow the spirit of the nodes_onto(), and in case of empty
> relmask, clean the ret mask and bail out
>
> I'm in a favor for the 1st option, because empty relmask looks buggy
> anyways.
>
> > The approach proposed here of just checking whether the node weight is 0
> > won't work for a few cases, namely for MPOL_DEFAULT and MPOL_PREFERRED where
> > empty nodemasks are actually allowed.
>
> Not sure I understand this. The mpol_relative_nodemask() is called
> only if MPOL_F_RELATIVE_NODES is set. In mpol_rebind_nodemask(), if
> both MPOL_F_STATIC_NODES and MPOL_F_RELATIVE_NODES are set, the former
> wins. How would the RELATIVE mode mess with the others?

Yes, you're right, the case that MPOL_DEFAULT and MPOL_PREFERRED allows empty
nodemasks is precisely when !STATIC && !RELATIVE :p this is my bad for missing
that case completely.

> Anyways, I'm not really deep in mempolicy domain, so please educate me if
> I miss something.

Thank you, I have also learned a lot looking into this to think about what the
best solution is!
Joshua