From: "Kiryl Shutsemau (Meta)" 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). 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 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. Bits declared in the enum are now safe to pass to mk_vma_flags() regardless of arch. Fixes: 9ea35a25d51b ("mm: introduce VMA flags bitmap type") Cc: stable@vger.kernel.org Signed-off-by: Kiryl Shutsemau --- include/linux/mm.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 0f2612a70fb1..71b11945e4fc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -286,8 +286,17 @@ extern unsigned int kobjsize(const void *objp); */ typedef int __bitwise vma_flag_t; -#define DECLARE_VMA_BIT(name, bitnum) \ - VMA_ ## name ## _BIT = ((__force vma_flag_t)bitnum) +/* + * VMA_NO_BIT means "no bit"; mk_vma_flags() skips it. DECLARE_VMA_BIT() + * below uses it for any bit number that doesn't fit in the bitmap, so + * callers don't need to track which bits are valid on the current build. + */ +#define VMA_NO_BIT ((__force vma_flag_t)-1) + +#define DECLARE_VMA_BIT(name, bitnum) \ + VMA_ ## name ## _BIT = (((bitnum) < NUM_VMA_FLAG_BITS) ? \ + ((__force vma_flag_t)(bitnum)) : \ + VMA_NO_BIT) #define DECLARE_VMA_BIT_ALIAS(name, aliased) \ VMA_ ## name ## _BIT = (VMA_ ## aliased ## _BIT) enum { @@ -1081,6 +1090,8 @@ static __always_inline void vma_flags_set_flag(vma_flags_t *flags, { unsigned long *bitmap = flags->__vma_flags; + if ((__force int)bit < 0) + return; __set_bit((__force int)bit, bitmap); } -- 2.54.0