This patch adds the ability to atomically set VMA flags with only the mmap
read/VMA read lock held.
As this could be hugely problematic for VMA flags in general given that all
other accesses are non-atomic and serialised by the mmap/VMA locks, we
implement this with a strict allow-list - that is, only designated flags
are allowed to do this.
We make VM_MAYBE_GUARD one of these flags.
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
include/linux/mm.h | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2a5516bff75a..699566c21ff7 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -518,6 +518,9 @@ extern unsigned int kobjsize(const void *objp);
/* This mask represents all the VMA flag bits used by mlock */
#define VM_LOCKED_MASK (VM_LOCKED | VM_LOCKONFAULT)
+/* These flags can be updated atomically via VMA/mmap read lock. */
+#define VM_ATOMIC_SET_ALLOWED VM_MAYBE_GUARD
+
/* Arch-specific flags to clear when updating VM flags on protection change */
#ifndef VM_ARCH_CLEAR
# define VM_ARCH_CLEAR VM_NONE
@@ -860,6 +863,45 @@ static inline void vm_flags_mod(struct vm_area_struct *vma,
__vm_flags_mod(vma, set, clear);
}
+static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma,
+ int bit)
+{
+ const vm_flags_t mask = BIT(bit);
+
+ /* Only specific flags are permitted */
+ if (WARN_ON_ONCE(!(mask & VM_ATOMIC_SET_ALLOWED)))
+ return false;
+
+ return true;
+}
+
+/*
+ * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific
+ * valid flags are allowed to do this.
+ */
+static inline void vma_flag_set_atomic(struct vm_area_struct *vma, int bit)
+{
+ /* mmap read lock/VMA read lock must be held. */
+ if (!rwsem_is_locked(&vma->vm_mm->mmap_lock))
+ vma_assert_locked(vma);
+
+ if (__vma_flag_atomic_valid(vma, bit))
+ set_bit(bit, &vma->__vm_flags);
+}
+
+/*
+ * Test for VMA flag atomically. Requires no locks. Only specific valid flags
+ * are allowed to do this.
+ *
+ * This is necessarily racey, so callers must ensure that serialisation is
+ * achieved through some other means, or that races are permissible.
+ */
+static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
+{
+ if (__vma_flag_atomic_valid(vma, bit))
+ return test_bit(bit, &vma->__vm_flags);
+}
+
static inline void vma_set_anonymous(struct vm_area_struct *vma)
{
vma->vm_ops = NULL;
--
2.51.0
Hi Andrew,
OK take 2 here :) please apply this fix-patch which both makes sparse happy and
avoids a clang compilation error on this commit.
Cheers, Lorenzo
----8<----
From 553fb3f0fc9f3c351bddf956b00d1dfaa2a32920 Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Date: Mon, 10 Nov 2025 17:35:11 +0000
Subject: [PATCH] fixup
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
include/linux/mm.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 699566c21ff7..a9b8f6205204 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -886,7 +886,7 @@ static inline void vma_flag_set_atomic(struct vm_area_struct *vma, int bit)
vma_assert_locked(vma);
if (__vma_flag_atomic_valid(vma, bit))
- set_bit(bit, &vma->__vm_flags);
+ set_bit(bit, &ACCESS_PRIVATE(vma, __vm_flags));
}
/*
@@ -899,7 +899,9 @@ static inline void vma_flag_set_atomic(struct vm_area_struct *vma, int bit)
static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
{
if (__vma_flag_atomic_valid(vma, bit))
- return test_bit(bit, &vma->__vm_flags);
+ return test_bit(bit, &vma->vm_flags);
+
+ return false;
}
static inline void vma_set_anonymous(struct vm_area_struct *vma)
--
2.51.0
Hi Andrew,
Please apply this trivial fix-patch.
Thanks, Lorenzo
----8<----
From e73da6d99f6e32c959c7a852a90f03c9c76816c6 Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Date: Mon, 10 Nov 2025 17:35:11 +0000
Subject: [PATCH] fixup
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
include/linux/mm.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 699566c21ff7..e94005f2b985 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -900,6 +900,8 @@ static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
{
if (__vma_flag_atomic_valid(vma, bit))
return test_bit(bit, &vma->__vm_flags);
+
+ return false;
}
static inline void vma_set_anonymous(struct vm_area_struct *vma)
--
2.51.0
Andrew - actually please ignore this, let me send another that'll fold this in
and make sparse happy too.
Cheers, Lorenzo
On Mon, Nov 10, 2025 at 05:36:29PM +0000, Lorenzo Stoakes wrote:
> Hi Andrew,
>
> Please apply this trivial fix-patch.
>
> Thanks, Lorenzo
>
> ----8<----
>
> From e73da6d99f6e32c959c7a852a90f03c9c76816c6 Mon Sep 17 00:00:00 2001
> From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Date: Mon, 10 Nov 2025 17:35:11 +0000
> Subject: [PATCH] fixup
>
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> ---
> include/linux/mm.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 699566c21ff7..e94005f2b985 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -900,6 +900,8 @@ static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
> {
> if (__vma_flag_atomic_valid(vma, bit))
> return test_bit(bit, &vma->__vm_flags);
> +
> + return false;
> }
>
> static inline void vma_set_anonymous(struct vm_area_struct *vma)
> --
> 2.51.0
On 11/7/25 17:11, Lorenzo Stoakes wrote:
> This patch adds the ability to atomically set VMA flags with only the mmap
> read/VMA read lock held.
>
> As this could be hugely problematic for VMA flags in general given that all
> other accesses are non-atomic and serialised by the mmap/VMA locks, we
> implement this with a strict allow-list - that is, only designated flags
> are allowed to do this.
>
> We make VM_MAYBE_GUARD one of these flags.
>
> Reviewed-by: Pedro Falcato <pfalcato@suse.de>
> Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> ---
> include/linux/mm.h | 42 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 2a5516bff75a..699566c21ff7 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -518,6 +518,9 @@ extern unsigned int kobjsize(const void *objp);
> /* This mask represents all the VMA flag bits used by mlock */
> #define VM_LOCKED_MASK (VM_LOCKED | VM_LOCKONFAULT)
>
> +/* These flags can be updated atomically via VMA/mmap read lock. */
> +#define VM_ATOMIC_SET_ALLOWED VM_MAYBE_GUARD
> +
> /* Arch-specific flags to clear when updating VM flags on protection change */
> #ifndef VM_ARCH_CLEAR
> # define VM_ARCH_CLEAR VM_NONE
> @@ -860,6 +863,45 @@ static inline void vm_flags_mod(struct vm_area_struct *vma,
> __vm_flags_mod(vma, set, clear);
> }
>
> +static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma,
> + int bit)
> +{
> + const vm_flags_t mask = BIT(bit);
> +
> + /* Only specific flags are permitted */
> + if (WARN_ON_ONCE(!(mask & VM_ATOMIC_SET_ALLOWED)))
> + return false;
> +
> + return true;
> +}
> +
> +/*
> + * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific
> + * valid flags are allowed to do this.
> + */
> +static inline void vma_flag_set_atomic(struct vm_area_struct *vma, int bit)
> +{
> + /* mmap read lock/VMA read lock must be held. */
> + if (!rwsem_is_locked(&vma->vm_mm->mmap_lock))
> + vma_assert_locked(vma);
> +
> + if (__vma_flag_atomic_valid(vma, bit))
> + set_bit(bit, &vma->__vm_flags);
> +}
> +
> +/*
> + * Test for VMA flag atomically. Requires no locks. Only specific valid flags
> + * are allowed to do this.
> + *
> + * This is necessarily racey, so callers must ensure that serialisation is
> + * achieved through some other means, or that races are permissible.
> + */
> +static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
> +{
> + if (__vma_flag_atomic_valid(vma, bit))
> + return test_bit(bit, &vma->__vm_flags);
> +}
Hm clang is unhappy here.
./include/linux/mm.h:932:1: error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type]
932 | }
| ^
1 error generated.
I don't have CONFIG_WERROR enabled though, so not sure why it's not just a
warning, as the function is unused until patch 5/8 which adds a "return
false" here. So it's just a potential bisection annoyance with clang.
Andrew could you move that hunk from to this patch? Thanks.
> +
> static inline void vma_set_anonymous(struct vm_area_struct *vma)
> {
> vma->vm_ops = NULL;
On Mon, Nov 10, 2025 at 04:51:27PM +0100, Vlastimil Babka wrote:
> On 11/7/25 17:11, Lorenzo Stoakes wrote:
> > This patch adds the ability to atomically set VMA flags with only the mmap
> > read/VMA read lock held.
> >
> > As this could be hugely problematic for VMA flags in general given that all
> > other accesses are non-atomic and serialised by the mmap/VMA locks, we
> > implement this with a strict allow-list - that is, only designated flags
> > are allowed to do this.
> >
> > We make VM_MAYBE_GUARD one of these flags.
> >
> > Reviewed-by: Pedro Falcato <pfalcato@suse.de>
> > Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
> > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> > ---
> > include/linux/mm.h | 42 ++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 42 insertions(+)
> >
> > diff --git a/include/linux/mm.h b/include/linux/mm.h
> > index 2a5516bff75a..699566c21ff7 100644
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -518,6 +518,9 @@ extern unsigned int kobjsize(const void *objp);
> > /* This mask represents all the VMA flag bits used by mlock */
> > #define VM_LOCKED_MASK (VM_LOCKED | VM_LOCKONFAULT)
> >
> > +/* These flags can be updated atomically via VMA/mmap read lock. */
> > +#define VM_ATOMIC_SET_ALLOWED VM_MAYBE_GUARD
> > +
> > /* Arch-specific flags to clear when updating VM flags on protection change */
> > #ifndef VM_ARCH_CLEAR
> > # define VM_ARCH_CLEAR VM_NONE
> > @@ -860,6 +863,45 @@ static inline void vm_flags_mod(struct vm_area_struct *vma,
> > __vm_flags_mod(vma, set, clear);
> > }
> >
> > +static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma,
> > + int bit)
> > +{
> > + const vm_flags_t mask = BIT(bit);
> > +
> > + /* Only specific flags are permitted */
> > + if (WARN_ON_ONCE(!(mask & VM_ATOMIC_SET_ALLOWED)))
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > +/*
> > + * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific
> > + * valid flags are allowed to do this.
> > + */
> > +static inline void vma_flag_set_atomic(struct vm_area_struct *vma, int bit)
> > +{
> > + /* mmap read lock/VMA read lock must be held. */
> > + if (!rwsem_is_locked(&vma->vm_mm->mmap_lock))
> > + vma_assert_locked(vma);
> > +
> > + if (__vma_flag_atomic_valid(vma, bit))
> > + set_bit(bit, &vma->__vm_flags);
> > +}
> > +
> > +/*
> > + * Test for VMA flag atomically. Requires no locks. Only specific valid flags
> > + * are allowed to do this.
> > + *
> > + * This is necessarily racey, so callers must ensure that serialisation is
> > + * achieved through some other means, or that races are permissible.
> > + */
> > +static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, int bit)
> > +{
> > + if (__vma_flag_atomic_valid(vma, bit))
> > + return test_bit(bit, &vma->__vm_flags);
> > +}
>
> Hm clang is unhappy here.
>
> ./include/linux/mm.h:932:1: error: non-void function does not return a value in all control paths [-Werror,-Wreturn-type]
> 932 | }
> | ^
> 1 error generated.
Yeah fun that gcc doesn't highlight this, god knows why not.
I thought I had fixed this (I remember it coming up in testing) but clearly I
fixed at the wrong commit.
>
> I don't have CONFIG_WERROR enabled though, so not sure why it's not just a
> warning, as the function is unused until patch 5/8 which adds a "return
> false" here. So it's just a potential bisection annoyance with clang.
>
> Andrew could you move that hunk from to this patch? Thanks.
I don't think this is the right solution.
Let's just add a return false. Will send a fix-patch.
>
> > +
> > static inline void vma_set_anonymous(struct vm_area_struct *vma)
> > {
> > vma->vm_ops = NULL;
>
Cheers, Lorenzo
© 2016 - 2025 Red Hat, Inc.