Re: [PATCH v5 04/18] mm: skip out-of-range bits in mk_vma_flags()
From: Mike Rapoport
Date: Sat May 30 2026 - 12:57:05 EST
On Fri, May 29, 2026 at 03:00:14PM +0100, Lorenzo Stoakes wrote:
> On Tue, May 26, 2026 at 02:04:52PM +0100, Kiryl Shutsemau wrote:
> > From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>
> >
> > vma_flags_t is one unsigned long on 32-bit -- NUM_VMA_FLAG_BITS ==
> > BITS_PER_LONG by design, so VM_xxx-declared bits sit in the first
> > word and hit the single-long fast path. But the bit enum declares
> > some bits unconditionally above BITS_PER_LONG (VMA_UFFD_MINOR_BIT
> > == 41 today, with VM_UFFD_MINOR == VM_NONE on 32-bit so no VMA
> > actually carries the bit).
>
> Yeah ugh.
>
> > Passing such a bit to mk_vma_flags() goes through __set_bit(41,
> > &one_long) and writes one word past the end. The compiler folds
> > the OOB store with wraparound (1UL << (41 % 32) == bit 9) into
> > the first word. Bit 9 is already in __VMA_UFFD_FLAGS so the mask
> > happens to come out right today, but any high-numbered bit whose
>
> That is... helpful :) but not great that this is the situation, an
> oversight, clearly! How I hate 32-bit kernels :)
>
> > mod-BITS_PER_LONG position is otherwise unused would silently OR
> > an extra bit into the mask.
> >
> > Add VMA_NO_BIT and have DECLARE_VMA_BIT() resolve any bitnum out
> > of range to it. vma_flags_set_flag() drops negative bit values.
> > The ternary collapses at compile time, the runtime check folds
> > away when the bit is in range, and the common path is unchanged.
>
> Hmm are you sure it does?
>
> A key design goal was that mk_vma_flags() generates compile-time constants
> the same as if the bitmap were constructed independently.
>
> This surely must generate code? Or at least runs a significant risk of it?
...
> A simple solution that doesn't require change to the core is to just uglify
> userfaultfd_k.h a bit with:
>
> #ifdef HAVE_ARCH_USERFAULTFD_MINOR
> #define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT, \
> VMA_UFFD_MINOR_BIT)
> #else
> #define __VMA_UFFD_FLAGS mk_vma_flags(VMA_UFFD_MISSING_BIT, VMA_UFFD_WP_BIT)
> #endif
>
> But of course that becomes much more horrible with your changes...
>
> Another alternative, which I used for VMA_DROPPABLE is to add something
> like this in mm.h:
>
> #ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
> #define VM_UFFD_MINOR INIT_VM_FLAG(UFFD_MINOR)
> +define VMA_UFFD_MINOR mk_vma_flags(VMA_UFFD_MINOR_BIT)
> #else
> #define VM_UFFD_MINOR VM_NONE
> +define VMA_UFFD_MINOR EMPTY_VMA_FLAGS
> #endif
I have a PoC of yet another alternative:
https://git.kernel.org/pub/scm/linux/kernel/git/rppt/linux.git/log/?h=uffd/vm-flags
The idea there is to keep a single VMA flag, VMA_UFFD_BIT/VM_UFFD and move
all the rest into what's now struct vm_userfaultfd_ctx.
--
Sincerely yours,
Mike.