Linux Security Modules development
 help / color / mirror / Atom feed
* Re: [PATCH v2 0/6] Landlock: Implement scope control for pathname Unix sockets
From: Mickaël Salaün @ 2026-02-09 20:20 UTC (permalink / raw)
  To: Tingmao Wang
  Cc: Günther Noack, Günther Noack, Justin Suess, Paul Moore,
	John Johansen, Demi Marie Obenour, Alyssa Ross, Jann Horn,
	Tahera Fahimi, Matthieu Buffet, linux-security-module
In-Reply-To: <c33926c5-3c7a-40d8-b910-ac99b43576ff@maowtm.org>

On Sun, Feb 08, 2026 at 08:48:22PM +0000, Tingmao Wang wrote:
> On 2/8/26 20:37, Günther Noack wrote:
> > On Sun, Feb 08, 2026 at 02:57:10AM +0000, Tingmao Wang wrote:
> >> On 2/5/26 10:27, Mickaël Salaün wrote:
> >>> On Thu, Feb 05, 2026 at 09:02:19AM +0100, Günther Noack wrote:
> >>>> [...]
> >>>>
> >>>> The implementation of this approach would be that we would have to
> >>>> join the functionality from the scoped and FS-based patch set, but
> >>>> without introducing the LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET flag in
> >>>> the UAPI.
> >>>
> >>> Right, this looks good to me.  We'll need to sync both patch series and
> >>> remove the scope flag from UAPI.  I'll let you and Tingmao work together
> >>> for the next series.  The "IPC scoping" documentation section should
> >>> mention LANDLOCK_ACCESS_FS_RESOLVE_UNIX even if it's not a scope flag.
> >>
> >> This sounds good to me.  I'm not sure how much code we can reuse out of
> >> the existing LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET patchset - but I think
> >> the selftest patches could still largely be useful (after changing e.g.
> >> create_scoped_domain() to use the RESOLVE_UNIX fs access instead of the
> >> scope bit for pathname sockets).  The fs-based rules (i.e. "exceptions")
> >> can then be tested separately from the scope tests (and would also check
> >> for things like path being different across mount namespaces etc).
> >>
> >> Günther, feel free to take anything out of the existing scope series, if
> >> you feel it would be useful.  Also let me know if you would like me to
> >> help with any part of the RESOLVE_UNIX series if you feel that would be
> >> useful (but you don't have to if not).
> >
> > Thank you, Tingmao!
> >
> > So far, the selftests that I already had in fs_test.c were
> > straightforward to extend so that they cover the new cases.  I had a
> > look at your patch set, but found the scoping tests difficult to port
> > to fs_test.c
> 
> I was thinking that the tests in scoped_abstract_unix_test.c could be
> extended to test scoping of pathname UNIX sockets as well (otherwise
> wouldn't you have to write another instance of the scoped_domains test
> based on scoped_base_variants.h, whether you put it in fs_test.c or
> somewhere else?)
> 
> And if you think that is sensible, then I'm hoping that patch 4,5,6 of the
> series would be mostly useful.  But it's up to you :)

I agree that these 3 patches should be integrated (see my other reply on
Günther's v4).

> 
> > , but I'll double check that we don't miss anything.
> > Either way, I'll make sure that you'll get appropriate credit for
> > it. :)
> 
> Thanks!
> 
> Tingmao
> 
> > ...
> 

^ permalink raw reply

* Re: [PATCH v2 12/13] tools/testing/vma: separate out vma_internal.h into logical headers
From: Liam R. Howlett @ 2026-02-09 20:18 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Andrew Morton, Jarkko Sakkinen, Dave Hansen, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, x86, H . Peter Anvin, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
	Matthew Brost, Alexander Viro, Christian Brauner, Jan Kara,
	Benjamin LaHaise, Gao Xiang, Chao Yu, Yue Hu, Jeffle Xu,
	Sandeep Dhavale, Hongbo Li, Chunhai Guo, Theodore Ts'o,
	Andreas Dilger, Muchun Song, Oscar Salvador, David Hildenbrand,
	Konstantin Komarov, Mike Marshall, Martin Brandenburg, Tony Luck,
	Reinette Chatre, Dave Martin, James Morse, Babu Moger,
	Carlos Maiolino, Damien Le Moal, Naohiro Aota, Johannes Thumshirn,
	Matthew Wilcox, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Hugh Dickins, Baolin Wang,
	Zi Yan, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
	Lance Yang, Jann Horn, Pedro Falcato, David Howells, Paul Moore,
	James Morris, Serge E . Hallyn, Yury Norov, Rasmus Villemoes,
	linux-sgx, linux-kernel, nvdimm, linux-cxl, dri-devel, intel-gfx,
	linux-fsdevel, linux-aio, linux-erofs, linux-ext4, linux-mm,
	ntfs3, devel, linux-xfs, keyrings, linux-security-module,
	Jason Gunthorpe
In-Reply-To: <dd57baf5b5986cb96a167150ac712cbe804b63ee.1769097829.git.lorenzo.stoakes@oracle.com>

* Lorenzo Stoakes <lorenzo.stoakes@oracle.com> [260122 16:06]:
> The vma_internal.h file is becoming entirely unmanageable. It combines
> duplicated kernel implementation logic that needs to be kept in-sync with
> the kernel, stubbed out declarations that we simply ignore for testing
> purposes and custom logic added to aid testing.
> 
> If we separate each of the three things into separate headers it makes
> things far more manageable, so do so:
> 
> * include/stubs.h  contains the stubbed declarations,
> * include/dup.h    contains the duplicated kernel declarations, and
> * include/custom.h contains declarations customised for testing.
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>

With the follow-up added on,

Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>

> ---
>  tools/testing/vma/Makefile         |    2 +-
>  tools/testing/vma/include/custom.h |  103 ++
>  tools/testing/vma/include/dup.h    | 1341 +++++++++++++++++++
>  tools/testing/vma/include/stubs.h  |  428 ++++++
>  tools/testing/vma/vma_internal.h   | 1936 +---------------------------
>  5 files changed, 1885 insertions(+), 1925 deletions(-)
>  create mode 100644 tools/testing/vma/include/custom.h
>  create mode 100644 tools/testing/vma/include/dup.h
>  create mode 100644 tools/testing/vma/include/stubs.h
> 
> diff --git a/tools/testing/vma/Makefile b/tools/testing/vma/Makefile
> index 94133d9d3955..50aa4301b3a6 100644
> --- a/tools/testing/vma/Makefile
> +++ b/tools/testing/vma/Makefile
> @@ -9,7 +9,7 @@ include ../shared/shared.mk
>  OFILES = $(SHARED_OFILES) main.o shared.o maple-shim.o
>  TARGETS = vma
>  
> -main.o: main.c shared.c shared.h vma_internal.h tests/merge.c tests/mmap.c tests/vma.c ../../../mm/vma.c ../../../mm/vma_init.c ../../../mm/vma_exec.c ../../../mm/vma.h
> +main.o: main.c shared.c shared.h vma_internal.h tests/merge.c tests/mmap.c tests/vma.c ../../../mm/vma.c ../../../mm/vma_init.c ../../../mm/vma_exec.c ../../../mm/vma.h include/custom.h include/dup.h include/stubs.h
>  
>  vma:	$(OFILES)
>  	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
> diff --git a/tools/testing/vma/include/custom.h b/tools/testing/vma/include/custom.h
> new file mode 100644
> index 000000000000..f567127efba9
> --- /dev/null
> +++ b/tools/testing/vma/include/custom.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#pragma once
> +
> +/*
> + * Contains declarations that exist in the kernel which have been CUSTOMISED for
> + * testing purposes to faciliate userland VMA testing.
> + */
> +
> +#ifdef CONFIG_MMU
> +extern unsigned long mmap_min_addr;
> +extern unsigned long dac_mmap_min_addr;
> +#else
> +#define mmap_min_addr		0UL
> +#define dac_mmap_min_addr	0UL
> +#endif
> +
> +#define VM_WARN_ON(_expr) (WARN_ON(_expr))
> +#define VM_WARN_ON_ONCE(_expr) (WARN_ON_ONCE(_expr))
> +#define VM_WARN_ON_VMG(_expr, _vmg) (WARN_ON(_expr))
> +#define VM_BUG_ON(_expr) (BUG_ON(_expr))
> +#define VM_BUG_ON_VMA(_expr, _vma) (BUG_ON(_expr))
> +
> +/* We hardcode this for now. */
> +#define sysctl_max_map_count 0x1000000UL
> +
> +#define TASK_SIZE ((1ul << 47)-PAGE_SIZE)
> +
> +/*
> + * The shared stubs do not implement this, it amounts to an fprintf(STDERR,...)
> + * either way :)
> + */
> +#define pr_warn_once pr_err
> +
> +#define pgtable_supports_soft_dirty() 1
> +
> +struct anon_vma {
> +	struct anon_vma *root;
> +	struct rb_root_cached rb_root;
> +
> +	/* Test fields. */
> +	bool was_cloned;
> +	bool was_unlinked;
> +};
> +
> +static inline void unlink_anon_vmas(struct vm_area_struct *vma)
> +{
> +	/* For testing purposes, indicate that the anon_vma was unlinked. */
> +	vma->anon_vma->was_unlinked = true;
> +}
> +
> +static inline void vma_start_write(struct vm_area_struct *vma)
> +{
> +	/* Used to indicate to tests that a write operation has begun. */
> +	vma->vm_lock_seq++;
> +}
> +
> +static inline __must_check
> +int vma_start_write_killable(struct vm_area_struct *vma)
> +{
> +	/* Used to indicate to tests that a write operation has begun. */
> +	vma->vm_lock_seq++;
> +	return 0;
> +}
> +
> +static inline int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src,
> +				 enum vma_operation operation)
> +{
> +	/* For testing purposes. We indicate that an anon_vma has been cloned. */
> +	if (src->anon_vma != NULL) {
> +		dst->anon_vma = src->anon_vma;
> +		dst->anon_vma->was_cloned = true;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline int __anon_vma_prepare(struct vm_area_struct *vma)
> +{
> +	struct anon_vma *anon_vma = calloc(1, sizeof(struct anon_vma));
> +
> +	if (!anon_vma)
> +		return -ENOMEM;
> +
> +	anon_vma->root = anon_vma;
> +	vma->anon_vma = anon_vma;
> +
> +	return 0;
> +}
> +
> +static inline int anon_vma_prepare(struct vm_area_struct *vma)
> +{
> +	if (likely(vma->anon_vma))
> +		return 0;
> +
> +	return __anon_vma_prepare(vma);
> +}
> +
> +static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt)
> +{
> +	if (reset_refcnt)
> +		refcount_set(&vma->vm_refcnt, 0);
> +}
> diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
> new file mode 100644
> index 000000000000..ed8708afb7af
> --- /dev/null
> +++ b/tools/testing/vma/include/dup.h
> @@ -0,0 +1,1341 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#pragma once
> +
> +/* Forward declarations to avoid header cycle. */
> +struct vm_area_struct;
> +static inline void vma_start_write(struct vm_area_struct *vma);
> +
> +extern const struct vm_operations_struct vma_dummy_vm_ops;
> +extern unsigned long stack_guard_gap;
> +extern const struct vm_operations_struct vma_dummy_vm_ops;
> +extern unsigned long rlimit(unsigned int limit);
> +struct task_struct *get_current(void);
> +
> +#define MMF_HAS_MDWE	28
> +#define current get_current()
> +
> +/*
> + * Define the task command name length as enum, then it can be visible to
> + * BPF programs.
> + */
> +enum {
> +	TASK_COMM_LEN = 16,
> +};
> +
> +/* PARTIALLY implemented types. */
> +struct mm_struct {
> +	struct maple_tree mm_mt;
> +	int map_count;			/* number of VMAs */
> +	unsigned long total_vm;	   /* Total pages mapped */
> +	unsigned long locked_vm;   /* Pages that have PG_mlocked set */
> +	unsigned long data_vm;	   /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
> +	unsigned long exec_vm;	   /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
> +	unsigned long stack_vm;	   /* VM_STACK */
> +
> +	unsigned long def_flags;
> +
> +	mm_flags_t flags; /* Must use mm_flags_* helpers to access */
> +};
> +struct address_space {
> +	struct rb_root_cached	i_mmap;
> +	unsigned long		flags;
> +	atomic_t		i_mmap_writable;
> +};
> +struct file_operations {
> +	int (*mmap)(struct file *, struct vm_area_struct *);
> +	int (*mmap_prepare)(struct vm_area_desc *);
> +};
> +struct file {
> +	struct address_space	*f_mapping;
> +	const struct file_operations	*f_op;
> +};
> +struct anon_vma_chain {
> +	struct anon_vma *anon_vma;
> +	struct list_head same_vma;
> +};
> +struct task_struct {
> +	char comm[TASK_COMM_LEN];
> +	pid_t pid;
> +	struct mm_struct *mm;
> +
> +	/* Used for emulating ABI behavior of previous Linux versions: */
> +	unsigned int			personality;
> +};
> +
> +struct kref {
> +	refcount_t refcount;
> +};
> +
> +struct anon_vma_name {
> +	struct kref kref;
> +	/* The name needs to be at the end because it is dynamically sized. */
> +	char name[];
> +};
> +
> +/*
> + * Contains declarations that are DUPLICATED from kernel source in order to
> + * faciliate userland VMA testing.
> + *
> + * These must be kept in sync with kernel source.
> + */
> +
> +#define VMA_LOCK_OFFSET	0x40000000
> +
> +typedef struct { unsigned long v; } freeptr_t;
> +
> +#define VM_NONE		0x00000000
> +
> +typedef int __bitwise vma_flag_t;
> +
> +#define ACCESS_PRIVATE(p, member) ((p)->member)
> +
> +#define DECLARE_VMA_BIT(name, bitnum) \
> +	VMA_ ## name ## _BIT = ((__force vma_flag_t)bitnum)
> +#define DECLARE_VMA_BIT_ALIAS(name, aliased) \
> +	VMA_ ## name ## _BIT = VMA_ ## aliased ## _BIT
> +enum {
> +	DECLARE_VMA_BIT(READ, 0),
> +	DECLARE_VMA_BIT(WRITE, 1),
> +	DECLARE_VMA_BIT(EXEC, 2),
> +	DECLARE_VMA_BIT(SHARED, 3),
> +	/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */
> +	DECLARE_VMA_BIT(MAYREAD, 4),	/* limits for mprotect() etc. */
> +	DECLARE_VMA_BIT(MAYWRITE, 5),
> +	DECLARE_VMA_BIT(MAYEXEC, 6),
> +	DECLARE_VMA_BIT(MAYSHARE, 7),
> +	DECLARE_VMA_BIT(GROWSDOWN, 8),	/* general info on the segment */
> +#ifdef CONFIG_MMU
> +	DECLARE_VMA_BIT(UFFD_MISSING, 9),/* missing pages tracking */
> +#else
> +	/* nommu: R/O MAP_PRIVATE mapping that might overlay a file mapping */
> +	DECLARE_VMA_BIT(MAYOVERLAY, 9),
> +#endif /* CONFIG_MMU */
> +	/* Page-ranges managed without "struct page", just pure PFN */
> +	DECLARE_VMA_BIT(PFNMAP, 10),
> +	DECLARE_VMA_BIT(MAYBE_GUARD, 11),
> +	DECLARE_VMA_BIT(UFFD_WP, 12),	/* wrprotect pages tracking */
> +	DECLARE_VMA_BIT(LOCKED, 13),
> +	DECLARE_VMA_BIT(IO, 14),	/* Memory mapped I/O or similar */
> +	DECLARE_VMA_BIT(SEQ_READ, 15),	/* App will access data sequentially */
> +	DECLARE_VMA_BIT(RAND_READ, 16),	/* App will not benefit from clustered reads */
> +	DECLARE_VMA_BIT(DONTCOPY, 17),	/* Do not copy this vma on fork */
> +	DECLARE_VMA_BIT(DONTEXPAND, 18),/* Cannot expand with mremap() */
> +	DECLARE_VMA_BIT(LOCKONFAULT, 19),/* Lock pages covered when faulted in */
> +	DECLARE_VMA_BIT(ACCOUNT, 20),	/* Is a VM accounted object */
> +	DECLARE_VMA_BIT(NORESERVE, 21),	/* should the VM suppress accounting */
> +	DECLARE_VMA_BIT(HUGETLB, 22),	/* Huge TLB Page VM */
> +	DECLARE_VMA_BIT(SYNC, 23),	/* Synchronous page faults */
> +	DECLARE_VMA_BIT(ARCH_1, 24),	/* Architecture-specific flag */
> +	DECLARE_VMA_BIT(WIPEONFORK, 25),/* Wipe VMA contents in child. */
> +	DECLARE_VMA_BIT(DONTDUMP, 26),	/* Do not include in the core dump */
> +	DECLARE_VMA_BIT(SOFTDIRTY, 27),	/* NOT soft dirty clean area */
> +	DECLARE_VMA_BIT(MIXEDMAP, 28),	/* Can contain struct page and pure PFN pages */
> +	DECLARE_VMA_BIT(HUGEPAGE, 29),	/* MADV_HUGEPAGE marked this vma */
> +	DECLARE_VMA_BIT(NOHUGEPAGE, 30),/* MADV_NOHUGEPAGE marked this vma */
> +	DECLARE_VMA_BIT(MERGEABLE, 31),	/* KSM may merge identical pages */
> +	/* These bits are reused, we define specific uses below. */
> +	DECLARE_VMA_BIT(HIGH_ARCH_0, 32),
> +	DECLARE_VMA_BIT(HIGH_ARCH_1, 33),
> +	DECLARE_VMA_BIT(HIGH_ARCH_2, 34),
> +	DECLARE_VMA_BIT(HIGH_ARCH_3, 35),
> +	DECLARE_VMA_BIT(HIGH_ARCH_4, 36),
> +	DECLARE_VMA_BIT(HIGH_ARCH_5, 37),
> +	DECLARE_VMA_BIT(HIGH_ARCH_6, 38),
> +	/*
> +	 * This flag is used to connect VFIO to arch specific KVM code. It
> +	 * indicates that the memory under this VMA is safe for use with any
> +	 * non-cachable memory type inside KVM. Some VFIO devices, on some
> +	 * platforms, are thought to be unsafe and can cause machine crashes
> +	 * if KVM does not lock down the memory type.
> +	 */
> +	DECLARE_VMA_BIT(ALLOW_ANY_UNCACHED, 39),
> +#ifdef CONFIG_PPC32
> +	DECLARE_VMA_BIT_ALIAS(DROPPABLE, ARCH_1),
> +#else
> +	DECLARE_VMA_BIT(DROPPABLE, 40),
> +#endif
> +	DECLARE_VMA_BIT(UFFD_MINOR, 41),
> +	DECLARE_VMA_BIT(SEALED, 42),
> +	/* Flags that reuse flags above. */
> +	DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),
> +	DECLARE_VMA_BIT_ALIAS(PKEY_BIT1, HIGH_ARCH_1),
> +	DECLARE_VMA_BIT_ALIAS(PKEY_BIT2, HIGH_ARCH_2),
> +	DECLARE_VMA_BIT_ALIAS(PKEY_BIT3, HIGH_ARCH_3),
> +	DECLARE_VMA_BIT_ALIAS(PKEY_BIT4, HIGH_ARCH_4),
> +#if defined(CONFIG_X86_USER_SHADOW_STACK)
> +	/*
> +	 * VM_SHADOW_STACK should not be set with VM_SHARED because of lack of
> +	 * support core mm.
> +	 *
> +	 * These VMAs will get a single end guard page. This helps userspace
> +	 * protect itself from attacks. A single page is enough for current
> +	 * shadow stack archs (x86). See the comments near alloc_shstk() in
> +	 * arch/x86/kernel/shstk.c for more details on the guard size.
> +	 */
> +	DECLARE_VMA_BIT_ALIAS(SHADOW_STACK, HIGH_ARCH_5),
> +#elif defined(CONFIG_ARM64_GCS)
> +	/*
> +	 * arm64's Guarded Control Stack implements similar functionality and
> +	 * has similar constraints to shadow stacks.
> +	 */
> +	DECLARE_VMA_BIT_ALIAS(SHADOW_STACK, HIGH_ARCH_6),
> +#endif
> +	DECLARE_VMA_BIT_ALIAS(SAO, ARCH_1),		/* Strong Access Ordering (powerpc) */
> +	DECLARE_VMA_BIT_ALIAS(GROWSUP, ARCH_1),		/* parisc */
> +	DECLARE_VMA_BIT_ALIAS(SPARC_ADI, ARCH_1),	/* sparc64 */
> +	DECLARE_VMA_BIT_ALIAS(ARM64_BTI, ARCH_1),	/* arm64 */
> +	DECLARE_VMA_BIT_ALIAS(ARCH_CLEAR, ARCH_1),	/* sparc64, arm64 */
> +	DECLARE_VMA_BIT_ALIAS(MAPPED_COPY, ARCH_1),	/* !CONFIG_MMU */
> +	DECLARE_VMA_BIT_ALIAS(MTE, HIGH_ARCH_4),	/* arm64 */
> +	DECLARE_VMA_BIT_ALIAS(MTE_ALLOWED, HIGH_ARCH_5),/* arm64 */
> +#ifdef CONFIG_STACK_GROWSUP
> +	DECLARE_VMA_BIT_ALIAS(STACK, GROWSUP),
> +	DECLARE_VMA_BIT_ALIAS(STACK_EARLY, GROWSDOWN),
> +#else
> +	DECLARE_VMA_BIT_ALIAS(STACK, GROWSDOWN),
> +#endif
> +};
> +
> +#define INIT_VM_FLAG(name) BIT((__force int) VMA_ ## name ## _BIT)
> +#define VM_READ		INIT_VM_FLAG(READ)
> +#define VM_WRITE	INIT_VM_FLAG(WRITE)
> +#define VM_EXEC		INIT_VM_FLAG(EXEC)
> +#define VM_SHARED	INIT_VM_FLAG(SHARED)
> +#define VM_MAYREAD	INIT_VM_FLAG(MAYREAD)
> +#define VM_MAYWRITE	INIT_VM_FLAG(MAYWRITE)
> +#define VM_MAYEXEC	INIT_VM_FLAG(MAYEXEC)
> +#define VM_MAYSHARE	INIT_VM_FLAG(MAYSHARE)
> +#define VM_GROWSDOWN	INIT_VM_FLAG(GROWSDOWN)
> +#ifdef CONFIG_MMU
> +#define VM_UFFD_MISSING	INIT_VM_FLAG(UFFD_MISSING)
> +#else
> +#define VM_UFFD_MISSING	VM_NONE
> +#define VM_MAYOVERLAY	INIT_VM_FLAG(MAYOVERLAY)
> +#endif
> +#define VM_PFNMAP	INIT_VM_FLAG(PFNMAP)
> +#define VM_MAYBE_GUARD	INIT_VM_FLAG(MAYBE_GUARD)
> +#define VM_UFFD_WP	INIT_VM_FLAG(UFFD_WP)
> +#define VM_LOCKED	INIT_VM_FLAG(LOCKED)
> +#define VM_IO		INIT_VM_FLAG(IO)
> +#define VM_SEQ_READ	INIT_VM_FLAG(SEQ_READ)
> +#define VM_RAND_READ	INIT_VM_FLAG(RAND_READ)
> +#define VM_DONTCOPY	INIT_VM_FLAG(DONTCOPY)
> +#define VM_DONTEXPAND	INIT_VM_FLAG(DONTEXPAND)
> +#define VM_LOCKONFAULT	INIT_VM_FLAG(LOCKONFAULT)
> +#define VM_ACCOUNT	INIT_VM_FLAG(ACCOUNT)
> +#define VM_NORESERVE	INIT_VM_FLAG(NORESERVE)
> +#define VM_HUGETLB	INIT_VM_FLAG(HUGETLB)
> +#define VM_SYNC		INIT_VM_FLAG(SYNC)
> +#define VM_ARCH_1	INIT_VM_FLAG(ARCH_1)
> +#define VM_WIPEONFORK	INIT_VM_FLAG(WIPEONFORK)
> +#define VM_DONTDUMP	INIT_VM_FLAG(DONTDUMP)
> +#ifdef CONFIG_MEM_SOFT_DIRTY
> +#define VM_SOFTDIRTY	INIT_VM_FLAG(SOFTDIRTY)
> +#else
> +#define VM_SOFTDIRTY	VM_NONE
> +#endif
> +#define VM_MIXEDMAP	INIT_VM_FLAG(MIXEDMAP)
> +#define VM_HUGEPAGE	INIT_VM_FLAG(HUGEPAGE)
> +#define VM_NOHUGEPAGE	INIT_VM_FLAG(NOHUGEPAGE)
> +#define VM_MERGEABLE	INIT_VM_FLAG(MERGEABLE)
> +#define VM_STACK	INIT_VM_FLAG(STACK)
> +#ifdef CONFIG_STACK_GROWS_UP
> +#define VM_STACK_EARLY	INIT_VM_FLAG(STACK_EARLY)
> +#else
> +#define VM_STACK_EARLY	VM_NONE
> +#endif
> +#ifdef CONFIG_ARCH_HAS_PKEYS
> +#define VM_PKEY_SHIFT ((__force int)VMA_HIGH_ARCH_0_BIT)
> +/* Despite the naming, these are FLAGS not bits. */
> +#define VM_PKEY_BIT0 INIT_VM_FLAG(PKEY_BIT0)
> +#define VM_PKEY_BIT1 INIT_VM_FLAG(PKEY_BIT1)
> +#define VM_PKEY_BIT2 INIT_VM_FLAG(PKEY_BIT2)
> +#if CONFIG_ARCH_PKEY_BITS > 3
> +#define VM_PKEY_BIT3 INIT_VM_FLAG(PKEY_BIT3)
> +#else
> +#define VM_PKEY_BIT3  VM_NONE
> +#endif /* CONFIG_ARCH_PKEY_BITS > 3 */
> +#if CONFIG_ARCH_PKEY_BITS > 4
> +#define VM_PKEY_BIT4 INIT_VM_FLAG(PKEY_BIT4)
> +#else
> +#define VM_PKEY_BIT4  VM_NONE
> +#endif /* CONFIG_ARCH_PKEY_BITS > 4 */
> +#endif /* CONFIG_ARCH_HAS_PKEYS */
> +#if defined(CONFIG_X86_USER_SHADOW_STACK) || defined(CONFIG_ARM64_GCS)
> +#define VM_SHADOW_STACK	INIT_VM_FLAG(SHADOW_STACK)
> +#else
> +#define VM_SHADOW_STACK	VM_NONE
> +#endif
> +#if defined(CONFIG_PPC64)
> +#define VM_SAO		INIT_VM_FLAG(SAO)
> +#elif defined(CONFIG_PARISC)
> +#define VM_GROWSUP	INIT_VM_FLAG(GROWSUP)
> +#elif defined(CONFIG_SPARC64)
> +#define VM_SPARC_ADI	INIT_VM_FLAG(SPARC_ADI)
> +#define VM_ARCH_CLEAR	INIT_VM_FLAG(ARCH_CLEAR)
> +#elif defined(CONFIG_ARM64)
> +#define VM_ARM64_BTI	INIT_VM_FLAG(ARM64_BTI)
> +#define VM_ARCH_CLEAR	INIT_VM_FLAG(ARCH_CLEAR)
> +#elif !defined(CONFIG_MMU)
> +#define VM_MAPPED_COPY	INIT_VM_FLAG(MAPPED_COPY)
> +#endif
> +#ifndef VM_GROWSUP
> +#define VM_GROWSUP	VM_NONE
> +#endif
> +#ifdef CONFIG_ARM64_MTE
> +#define VM_MTE		INIT_VM_FLAG(MTE)
> +#define VM_MTE_ALLOWED	INIT_VM_FLAG(MTE_ALLOWED)
> +#else
> +#define VM_MTE		VM_NONE
> +#define VM_MTE_ALLOWED	VM_NONE
> +#endif
> +#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
> +#define VM_UFFD_MINOR	INIT_VM_FLAG(UFFD_MINOR)
> +#else
> +#define VM_UFFD_MINOR	VM_NONE
> +#endif
> +#ifdef CONFIG_64BIT
> +#define VM_ALLOW_ANY_UNCACHED	INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
> +#define VM_SEALED		INIT_VM_FLAG(SEALED)
> +#else
> +#define VM_ALLOW_ANY_UNCACHED	VM_NONE
> +#define VM_SEALED		VM_NONE
> +#endif
> +#if defined(CONFIG_64BIT) || defined(CONFIG_PPC32)
> +#define VM_DROPPABLE		INIT_VM_FLAG(DROPPABLE)
> +#else
> +#define VM_DROPPABLE		VM_NONE
> +#endif
> +
> +/* Bits set in the VMA until the stack is in its final location */
> +#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY)
> +
> +#define TASK_EXEC ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0)
> +
> +/* Common data flag combinations */
> +#define VM_DATA_FLAGS_TSK_EXEC	(VM_READ | VM_WRITE | TASK_EXEC | \
> +				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> +#define VM_DATA_FLAGS_NON_EXEC	(VM_READ | VM_WRITE | VM_MAYREAD | \
> +				 VM_MAYWRITE | VM_MAYEXEC)
> +#define VM_DATA_FLAGS_EXEC	(VM_READ | VM_WRITE | VM_EXEC | \
> +				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> +
> +#ifndef VM_DATA_DEFAULT_FLAGS		/* arch can override this */
> +#define VM_DATA_DEFAULT_FLAGS  VM_DATA_FLAGS_EXEC
> +#endif
> +
> +#ifndef VM_STACK_DEFAULT_FLAGS		/* arch can override this */
> +#define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
> +#endif
> +
> +#define VM_STARTGAP_FLAGS (VM_GROWSDOWN | VM_SHADOW_STACK)
> +
> +#define VM_STACK_FLAGS	(VM_STACK | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
> +
> +/* VMA basic access permission flags */
> +#define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC)
> +
> +/*
> + * Special vmas that are non-mergable, non-mlock()able.
> + */
> +#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_PFNMAP | VM_MIXEDMAP)
> +
> +#define DEFAULT_MAP_WINDOW	((1UL << 47) - PAGE_SIZE)
> +#define TASK_SIZE_LOW		DEFAULT_MAP_WINDOW
> +#define TASK_SIZE_MAX		DEFAULT_MAP_WINDOW
> +#define STACK_TOP		TASK_SIZE_LOW
> +#define STACK_TOP_MAX		TASK_SIZE_MAX
> +
> +/* This mask represents all the VMA flag bits used by mlock */
> +#define VM_LOCKED_MASK	(VM_LOCKED | VM_LOCKONFAULT)
> +
> +#define TASK_EXEC ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0)
> +
> +#define VM_DATA_FLAGS_TSK_EXEC	(VM_READ | VM_WRITE | TASK_EXEC | \
> +				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> +
> +#define RLIMIT_STACK		3	/* max stack size */
> +#define RLIMIT_MEMLOCK		8	/* max locked-in-memory address space */
> +
> +#define CAP_IPC_LOCK         14
> +
> +#define VM_STICKY (VM_SOFTDIRTY | VM_MAYBE_GUARD)
> +
> +#define VM_IGNORE_MERGE VM_STICKY
> +
> +#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_MAYBE_GUARD)
> +
> +#define pgprot_val(x)		((x).pgprot)
> +#define __pgprot(x)		((pgprot_t) { (x) } )
> +
> +#define for_each_vma(__vmi, __vma)					\
> +	while (((__vma) = vma_next(&(__vmi))) != NULL)
> +
> +/* The MM code likes to work with exclusive end addresses */
> +#define for_each_vma_range(__vmi, __vma, __end)				\
> +	while (((__vma) = vma_find(&(__vmi), (__end))) != NULL)
> +
> +#define offset_in_page(p)	((unsigned long)(p) & ~PAGE_MASK)
> +
> +#define PHYS_PFN(x)	((unsigned long)((x) >> PAGE_SHIFT))
> +
> +#define test_and_set_bit(nr, addr) __test_and_set_bit(nr, addr)
> +#define test_and_clear_bit(nr, addr) __test_and_clear_bit(nr, addr)
> +
> +#define AS_MM_ALL_LOCKS 2
> +
> +#define swap(a, b) \
> +	do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
> +
> +/*
> + * Flags for bug emulation.
> + *
> + * These occupy the top three bytes.
> + */
> +enum {
> +	READ_IMPLIES_EXEC =	0x0400000,
> +};
> +
> +struct vma_iterator {
> +	struct ma_state mas;
> +};
> +
> +#define VMA_ITERATOR(name, __mm, __addr)				\
> +	struct vma_iterator name = {					\
> +		.mas = {						\
> +			.tree = &(__mm)->mm_mt,				\
> +			.index = __addr,				\
> +			.node = NULL,					\
> +			.status = ma_start,				\
> +		},							\
> +	}
> +
> +#define DEFINE_MUTEX(mutexname) \
> +	struct mutex mutexname = {}
> +
> +#define DECLARE_BITMAP(name, bits) \
> +	unsigned long name[BITS_TO_LONGS(bits)]
> +
> +#define EMPTY_VMA_FLAGS ((vma_flags_t){ })
> +
> +/* What action should be taken after an .mmap_prepare call is complete? */
> +enum mmap_action_type {
> +	MMAP_NOTHING,		/* Mapping is complete, no further action. */
> +	MMAP_REMAP_PFN,		/* Remap PFN range. */
> +	MMAP_IO_REMAP_PFN,	/* I/O remap PFN range. */
> +};
> +
> +/*
> + * Describes an action an mmap_prepare hook can instruct to be taken to complete
> + * the mapping of a VMA. Specified in vm_area_desc.
> + */
> +struct mmap_action {
> +	union {
> +		/* Remap range. */
> +		struct {
> +			unsigned long start;
> +			unsigned long start_pfn;
> +			unsigned long size;
> +			pgprot_t pgprot;
> +		} remap;
> +	};
> +	enum mmap_action_type type;
> +
> +	/*
> +	 * If specified, this hook is invoked after the selected action has been
> +	 * successfully completed. Note that the VMA write lock still held.
> +	 *
> +	 * The absolute minimum ought to be done here.
> +	 *
> +	 * Returns 0 on success, or an error code.
> +	 */
> +	int (*success_hook)(const struct vm_area_struct *vma);
> +
> +	/*
> +	 * If specified, this hook is invoked when an error occurred when
> +	 * attempting the selection action.
> +	 *
> +	 * The hook can return an error code in order to filter the error, but
> +	 * it is not valid to clear the error here.
> +	 */
> +	int (*error_hook)(int err);
> +
> +	/*
> +	 * This should be set in rare instances where the operation required
> +	 * that the rmap should not be able to access the VMA until
> +	 * completely set up.
> +	 */
> +	bool hide_from_rmap_until_complete :1;
> +};
> +
> +/* Operations which modify VMAs. */
> +enum vma_operation {
> +	VMA_OP_SPLIT,
> +	VMA_OP_MERGE_UNFAULTED,
> +	VMA_OP_REMAP,
> +	VMA_OP_FORK,
> +};
> +
> +/*
> + * Describes a VMA that is about to be mmap()'ed. Drivers may choose to
> + * manipulate mutable fields which will cause those fields to be updated in the
> + * resultant VMA.
> + *
> + * Helper functions are not required for manipulating any field.
> + */
> +struct vm_area_desc {
> +	/* Immutable state. */
> +	const struct mm_struct *const mm;
> +	struct file *const file; /* May vary from vm_file in stacked callers. */
> +	unsigned long start;
> +	unsigned long end;
> +
> +	/* Mutable fields. Populated with initial state. */
> +	pgoff_t pgoff;
> +	struct file *vm_file;
> +	union {
> +		vm_flags_t vm_flags;
> +		vma_flags_t vma_flags;
> +	};
> +	pgprot_t page_prot;
> +
> +	/* Write-only fields. */
> +	const struct vm_operations_struct *vm_ops;
> +	void *private_data;
> +
> +	/* Take further action? */
> +	struct mmap_action action;
> +};
> +
> +struct vm_area_struct {
> +	/* The first cache line has the info for VMA tree walking. */
> +
> +	union {
> +		struct {
> +			/* VMA covers [vm_start; vm_end) addresses within mm */
> +			unsigned long vm_start;
> +			unsigned long vm_end;
> +		};
> +		freeptr_t vm_freeptr; /* Pointer used by SLAB_TYPESAFE_BY_RCU */
> +	};
> +
> +	struct mm_struct *vm_mm;	/* The address space we belong to. */
> +	pgprot_t vm_page_prot;          /* Access permissions of this VMA. */
> +
> +	/*
> +	 * Flags, see mm.h.
> +	 * To modify use vm_flags_{init|reset|set|clear|mod} functions.
> +	 */
> +	union {
> +		const vm_flags_t vm_flags;
> +		vma_flags_t flags;
> +	};
> +
> +#ifdef CONFIG_PER_VMA_LOCK
> +	/*
> +	 * Can only be written (using WRITE_ONCE()) while holding both:
> +	 *  - mmap_lock (in write mode)
> +	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set
> +	 * Can be read reliably while holding one of:
> +	 *  - mmap_lock (in read or write mode)
> +	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set or vm_refcnt > 1
> +	 * Can be read unreliably (using READ_ONCE()) for pessimistic bailout
> +	 * while holding nothing (except RCU to keep the VMA struct allocated).
> +	 *
> +	 * This sequence counter is explicitly allowed to overflow; sequence
> +	 * counter reuse can only lead to occasional unnecessary use of the
> +	 * slowpath.
> +	 */
> +	unsigned int vm_lock_seq;
> +#endif
> +
> +	/*
> +	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
> +	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
> +	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
> +	 * or brk vma (with NULL file) can only be in an anon_vma list.
> +	 */
> +	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
> +					  * page_table_lock */
> +	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */
> +
> +	/* Function pointers to deal with this struct. */
> +	const struct vm_operations_struct *vm_ops;
> +
> +	/* Information about our backing store: */
> +	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
> +					   units */
> +	struct file * vm_file;		/* File we map to (can be NULL). */
> +	void * vm_private_data;		/* was vm_pte (shared mem) */
> +
> +#ifdef CONFIG_SWAP
> +	atomic_long_t swap_readahead_info;
> +#endif
> +#ifndef CONFIG_MMU
> +	struct vm_region *vm_region;	/* NOMMU mapping region */
> +#endif
> +#ifdef CONFIG_NUMA
> +	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
> +#endif
> +#ifdef CONFIG_NUMA_BALANCING
> +	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
> +#endif
> +#ifdef CONFIG_PER_VMA_LOCK
> +	/* Unstable RCU readers are allowed to read this. */
> +	refcount_t vm_refcnt;
> +#endif
> +	/*
> +	 * For areas with an address space and backing store,
> +	 * linkage into the address_space->i_mmap interval tree.
> +	 *
> +	 */
> +	struct {
> +		struct rb_node rb;
> +		unsigned long rb_subtree_last;
> +	} shared;
> +#ifdef CONFIG_ANON_VMA_NAME
> +	/*
> +	 * For private and shared anonymous mappings, a pointer to a null
> +	 * terminated string containing the name given to the vma, or NULL if
> +	 * unnamed. Serialized by mmap_lock. Use anon_vma_name to access.
> +	 */
> +	struct anon_vma_name *anon_name;
> +#endif
> +	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
> +} __randomize_layout;
> +
> +struct vm_operations_struct {
> +	void (*open)(struct vm_area_struct * area);
> +	/**
> +	 * @close: Called when the VMA is being removed from the MM.
> +	 * Context: User context.  May sleep.  Caller holds mmap_lock.
> +	 */
> +	void (*close)(struct vm_area_struct * area);
> +	/* Called any time before splitting to check if it's allowed */
> +	int (*may_split)(struct vm_area_struct *area, unsigned long addr);
> +	int (*mremap)(struct vm_area_struct *area);
> +	/*
> +	 * Called by mprotect() to make driver-specific permission
> +	 * checks before mprotect() is finalised.   The VMA must not
> +	 * be modified.  Returns 0 if mprotect() can proceed.
> +	 */
> +	int (*mprotect)(struct vm_area_struct *vma, unsigned long start,
> +			unsigned long end, unsigned long newflags);
> +	vm_fault_t (*fault)(struct vm_fault *vmf);
> +	vm_fault_t (*huge_fault)(struct vm_fault *vmf, unsigned int order);
> +	vm_fault_t (*map_pages)(struct vm_fault *vmf,
> +			pgoff_t start_pgoff, pgoff_t end_pgoff);
> +	unsigned long (*pagesize)(struct vm_area_struct * area);
> +
> +	/* notification that a previously read-only page is about to become
> +	 * writable, if an error is returned it will cause a SIGBUS */
> +	vm_fault_t (*page_mkwrite)(struct vm_fault *vmf);
> +
> +	/* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */
> +	vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf);
> +
> +	/* called by access_process_vm when get_user_pages() fails, typically
> +	 * for use by special VMAs. See also generic_access_phys() for a generic
> +	 * implementation useful for any iomem mapping.
> +	 */
> +	int (*access)(struct vm_area_struct *vma, unsigned long addr,
> +		      void *buf, int len, int write);
> +
> +	/* Called by the /proc/PID/maps code to ask the vma whether it
> +	 * has a special name.  Returning non-NULL will also cause this
> +	 * vma to be dumped unconditionally. */
> +	const char *(*name)(struct vm_area_struct *vma);
> +
> +#ifdef CONFIG_NUMA
> +	/*
> +	 * set_policy() op must add a reference to any non-NULL @new mempolicy
> +	 * to hold the policy upon return.  Caller should pass NULL @new to
> +	 * remove a policy and fall back to surrounding context--i.e. do not
> +	 * install a MPOL_DEFAULT policy, nor the task or system default
> +	 * mempolicy.
> +	 */
> +	int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
> +
> +	/*
> +	 * get_policy() op must add reference [mpol_get()] to any policy at
> +	 * (vma,addr) marked as MPOL_SHARED.  The shared policy infrastructure
> +	 * in mm/mempolicy.c will do this automatically.
> +	 * get_policy() must NOT add a ref if the policy at (vma,addr) is not
> +	 * marked as MPOL_SHARED. vma policies are protected by the mmap_lock.
> +	 * If no [shared/vma] mempolicy exists at the addr, get_policy() op
> +	 * must return NULL--i.e., do not "fallback" to task or system default
> +	 * policy.
> +	 */
> +	struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
> +					unsigned long addr, pgoff_t *ilx);
> +#endif
> +#ifdef CONFIG_FIND_NORMAL_PAGE
> +	/*
> +	 * Called by vm_normal_page() for special PTEs in @vma at @addr. This
> +	 * allows for returning a "normal" page from vm_normal_page() even
> +	 * though the PTE indicates that the "struct page" either does not exist
> +	 * or should not be touched: "special".
> +	 *
> +	 * Do not add new users: this really only works when a "normal" page
> +	 * was mapped, but then the PTE got changed to something weird (+
> +	 * marked special) that would not make pte_pfn() identify the originally
> +	 * inserted page.
> +	 */
> +	struct page *(*find_normal_page)(struct vm_area_struct *vma,
> +					 unsigned long addr);
> +#endif /* CONFIG_FIND_NORMAL_PAGE */
> +};
> +
> +struct vm_unmapped_area_info {
> +#define VM_UNMAPPED_AREA_TOPDOWN 1
> +	unsigned long flags;
> +	unsigned long length;
> +	unsigned long low_limit;
> +	unsigned long high_limit;
> +	unsigned long align_mask;
> +	unsigned long align_offset;
> +	unsigned long start_gap;
> +};
> +
> +struct pagetable_move_control {
> +	struct vm_area_struct *old; /* Source VMA. */
> +	struct vm_area_struct *new; /* Destination VMA. */
> +	unsigned long old_addr; /* Address from which the move begins. */
> +	unsigned long old_end; /* Exclusive address at which old range ends. */
> +	unsigned long new_addr; /* Address to move page tables to. */
> +	unsigned long len_in; /* Bytes to remap specified by user. */
> +
> +	bool need_rmap_locks; /* Do rmap locks need to be taken? */
> +	bool for_stack; /* Is this an early temp stack being moved? */
> +};
> +
> +#define PAGETABLE_MOVE(name, old_, new_, old_addr_, new_addr_, len_)	\
> +	struct pagetable_move_control name = {				\
> +		.old = old_,						\
> +		.new = new_,						\
> +		.old_addr = old_addr_,					\
> +		.old_end = (old_addr_) + (len_),			\
> +		.new_addr = new_addr_,					\
> +		.len_in = len_,						\
> +	}
> +
> +static inline void vma_iter_invalidate(struct vma_iterator *vmi)
> +{
> +	mas_pause(&vmi->mas);
> +}
> +
> +static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
> +{
> +	return __pgprot(pgprot_val(oldprot) | pgprot_val(newprot));
> +}
> +
> +static inline pgprot_t vm_get_page_prot(vm_flags_t vm_flags)
> +{
> +	return __pgprot(vm_flags);
> +}
> +
> +static inline bool mm_flags_test(int flag, const struct mm_struct *mm)
> +{
> +	return test_bit(flag, ACCESS_PRIVATE(&mm->flags, __mm_flags));
> +}
> +
> +/*
> + * Copy value to the first system word of VMA flags, non-atomically.
> + *
> + * IMPORTANT: This does not overwrite bytes past the first system word. The
> + * caller must account for this.
> + */
> +static inline void vma_flags_overwrite_word(vma_flags_t *flags, unsigned long value)
> +{
> +	*ACCESS_PRIVATE(flags, __vma_flags) = value;
> +}
> +
> +/*
> + * Copy value to the first system word of VMA flags ONCE, non-atomically.
> + *
> + * IMPORTANT: This does not overwrite bytes past the first system word. The
> + * caller must account for this.
> + */
> +static inline void vma_flags_overwrite_word_once(vma_flags_t *flags, unsigned long value)
> +{
> +	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> +
> +	WRITE_ONCE(*bitmap, value);
> +}
> +
> +/* Update the first system word of VMA flags setting bits, non-atomically. */
> +static inline void vma_flags_set_word(vma_flags_t *flags, unsigned long value)
> +{
> +	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> +
> +	*bitmap |= value;
> +}
> +
> +/* Update the first system word of VMA flags clearing bits, non-atomically. */
> +static inline void vma_flags_clear_word(vma_flags_t *flags, unsigned long value)
> +{
> +	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> +
> +	*bitmap &= ~value;
> +}
> +
> +static inline void vma_flags_clear_all(vma_flags_t *flags)
> +{
> +	bitmap_zero(ACCESS_PRIVATE(flags, __vma_flags), NUM_VMA_FLAG_BITS);
> +}
> +
> +static inline void vma_flag_set(vma_flags_t *flags, vma_flag_t bit)
> +{
> +	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> +
> +	__set_bit((__force int)bit, bitmap);
> +}
> +
> +/* Use when VMA is not part of the VMA tree and needs no locking */
> +static inline void vm_flags_init(struct vm_area_struct *vma,
> +				 vm_flags_t flags)
> +{
> +	vma_flags_clear_all(&vma->flags);
> +	vma_flags_overwrite_word(&vma->flags, flags);
> +}
> +
> +/*
> + * Use when VMA is part of the VMA tree and modifications need coordination
> + * Note: vm_flags_reset and vm_flags_reset_once do not lock the vma and
> + * it should be locked explicitly beforehand.
> + */
> +static inline void vm_flags_reset(struct vm_area_struct *vma,
> +				  vm_flags_t flags)
> +{
> +	vma_assert_write_locked(vma);
> +	vm_flags_init(vma, flags);
> +}
> +
> +static inline void vm_flags_reset_once(struct vm_area_struct *vma,
> +				       vm_flags_t flags)
> +{
> +	vma_assert_write_locked(vma);
> +	/*
> +	 * The user should only be interested in avoiding reordering of
> +	 * assignment to the first word.
> +	 */
> +	vma_flags_clear_all(&vma->flags);
> +	vma_flags_overwrite_word_once(&vma->flags, flags);
> +}
> +
> +static inline void vm_flags_set(struct vm_area_struct *vma,
> +				vm_flags_t flags)
> +{
> +	vma_start_write(vma);
> +	vma_flags_set_word(&vma->flags, flags);
> +}
> +
> +static inline void vm_flags_clear(struct vm_area_struct *vma,
> +				  vm_flags_t flags)
> +{
> +	vma_start_write(vma);
> +	vma_flags_clear_word(&vma->flags, flags);
> +}
> +
> +static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits)
> +{
> +	vma_flags_t flags;
> +	int i;
> +
> +	vma_flags_clear_all(&flags);
> +	for (i = 0; i < count; i++)
> +		vma_flag_set(&flags, bits[i]);
> +	return flags;
> +}
> +
> +#define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \
> +					 (const vma_flag_t []){__VA_ARGS__})
> +
> +static __always_inline bool vma_flags_test_mask(const vma_flags_t *flags,
> +		vma_flags_t to_test)
> +{
> +	const unsigned long *bitmap = flags->__vma_flags;
> +	const unsigned long *bitmap_to_test = to_test.__vma_flags;
> +
> +	return bitmap_intersects(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS);
> +}
> +
> +#define vma_flags_test(flags, ...) \
> +	vma_flags_test_mask(flags, mk_vma_flags(__VA_ARGS__))
> +
> +static __always_inline bool vma_flags_test_all_mask(const vma_flags_t *flags,
> +		vma_flags_t to_test)
> +{
> +	const unsigned long *bitmap = flags->__vma_flags;
> +	const unsigned long *bitmap_to_test = to_test.__vma_flags;
> +
> +	return bitmap_subset(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS);
> +}
> +
> +#define vma_flags_test_all(flags, ...) \
> +	vma_flags_test_all_mask(flags, mk_vma_flags(__VA_ARGS__))
> +
> +static __always_inline void vma_flags_set_mask(vma_flags_t *flags, vma_flags_t to_set)
> +{
> +	unsigned long *bitmap = flags->__vma_flags;
> +	const unsigned long *bitmap_to_set = to_set.__vma_flags;
> +
> +	bitmap_or(bitmap, bitmap, bitmap_to_set, NUM_VMA_FLAG_BITS);
> +}
> +
> +#define vma_flags_set(flags, ...) \
> +	vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__))
> +
> +static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear)
> +{
> +	unsigned long *bitmap = flags->__vma_flags;
> +	const unsigned long *bitmap_to_clear = to_clear.__vma_flags;
> +
> +	bitmap_andnot(bitmap, bitmap, bitmap_to_clear, NUM_VMA_FLAG_BITS);
> +}
> +
> +#define vma_flags_clear(flags, ...) \
> +	vma_flags_clear_mask(flags, mk_vma_flags(__VA_ARGS__))
> +
> +static inline bool vma_test_all_flags_mask(const struct vm_area_struct *vma,
> +					   vma_flags_t flags)
> +{
> +	return vma_flags_test_all_mask(&vma->flags, flags);
> +}
> +
> +#define vma_test_all_flags(vma, ...) \
> +	vma_test_all_flags_mask(vma, mk_vma_flags(__VA_ARGS__))
> +
> +static inline bool is_shared_maywrite_vm_flags(vm_flags_t vm_flags)
> +{
> +	return (vm_flags & (VM_SHARED | VM_MAYWRITE)) ==
> +		(VM_SHARED | VM_MAYWRITE);
> +}
> +
> +static inline void vma_set_flags_mask(struct vm_area_struct *vma,
> +				      vma_flags_t flags)
> +{
> +	vma_flags_set_mask(&vma->flags, flags);
> +}
> +
> +#define vma_set_flags(vma, ...) \
> +	vma_set_flags_mask(vma, mk_vma_flags(__VA_ARGS__))
> +
> +static inline bool vma_desc_test_flags_mask(const struct vm_area_desc *desc,
> +					    vma_flags_t flags)
> +{
> +	return vma_flags_test_mask(&desc->vma_flags, flags);
> +}
> +
> +#define vma_desc_test_flags(desc, ...) \
> +	vma_desc_test_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> +
> +static inline void vma_desc_set_flags_mask(struct vm_area_desc *desc,
> +					   vma_flags_t flags)
> +{
> +	vma_flags_set_mask(&desc->vma_flags, flags);
> +}
> +
> +#define vma_desc_set_flags(desc, ...) \
> +	vma_desc_set_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> +
> +static inline void vma_desc_clear_flags_mask(struct vm_area_desc *desc,
> +					     vma_flags_t flags)
> +{
> +	vma_flags_clear_mask(&desc->vma_flags, flags);
> +}
> +
> +#define vma_desc_clear_flags(desc, ...) \
> +	vma_desc_clear_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> +
> +static inline bool is_shared_maywrite(const vma_flags_t *flags)
> +{
> +	return vma_flags_test_all(flags, VMA_SHARED_BIT, VMA_MAYWRITE_BIT);
> +}
> +
> +static inline bool vma_is_shared_maywrite(struct vm_area_struct *vma)
> +{
> +	return is_shared_maywrite(&vma->flags);
> +}
> +
> +static inline struct vm_area_struct *vma_next(struct vma_iterator *vmi)
> +{
> +	/*
> +	 * Uses mas_find() to get the first VMA when the iterator starts.
> +	 * Calling mas_next() could skip the first entry.
> +	 */
> +	return mas_find(&vmi->mas, ULONG_MAX);
> +}
> +
> +/*
> + * WARNING: to avoid racing with vma_mark_attached()/vma_mark_detached(), these
> + * assertions should be made either under mmap_write_lock or when the object
> + * has been isolated under mmap_write_lock, ensuring no competing writers.
> + */
> +static inline void vma_assert_attached(struct vm_area_struct *vma)
> +{
> +	WARN_ON_ONCE(!refcount_read(&vma->vm_refcnt));
> +}
> +
> +static inline void vma_assert_detached(struct vm_area_struct *vma)
> +{
> +	WARN_ON_ONCE(refcount_read(&vma->vm_refcnt));
> +}
> +
> +static inline void vma_assert_write_locked(struct vm_area_struct *);
> +static inline void vma_mark_attached(struct vm_area_struct *vma)
> +{
> +	vma_assert_write_locked(vma);
> +	vma_assert_detached(vma);
> +	refcount_set_release(&vma->vm_refcnt, 1);
> +}
> +
> +static inline void vma_mark_detached(struct vm_area_struct *vma)
> +{
> +	vma_assert_write_locked(vma);
> +	vma_assert_attached(vma);
> +	/* We are the only writer, so no need to use vma_refcount_put(). */
> +	if (unlikely(!refcount_dec_and_test(&vma->vm_refcnt))) {
> +		/*
> +		 * Reader must have temporarily raised vm_refcnt but it will
> +		 * drop it without using the vma since vma is write-locked.
> +		 */
> +	}
> +}
> +
> +static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
> +{
> +	memset(vma, 0, sizeof(*vma));
> +	vma->vm_mm = mm;
> +	vma->vm_ops = &vma_dummy_vm_ops;
> +	INIT_LIST_HEAD(&vma->anon_vma_chain);
> +	vma->vm_lock_seq = UINT_MAX;
> +}
> +
> +/*
> + * These are defined in vma.h, but sadly vm_stat_account() is referenced by
> + * kernel/fork.c, so we have to these broadly available there, and temporarily
> + * define them here to resolve the dependency cycle.
> + */
> +#define is_exec_mapping(flags) \
> +	((flags & (VM_EXEC | VM_WRITE | VM_STACK)) == VM_EXEC)
> +
> +#define is_stack_mapping(flags) \
> +	(((flags & VM_STACK) == VM_STACK) || (flags & VM_SHADOW_STACK))
> +
> +#define is_data_mapping(flags) \
> +	((flags & (VM_WRITE | VM_SHARED | VM_STACK)) == VM_WRITE)
> +
> +static inline void vm_stat_account(struct mm_struct *mm, vm_flags_t flags,
> +				   long npages)
> +{
> +	WRITE_ONCE(mm->total_vm, READ_ONCE(mm->total_vm)+npages);
> +
> +	if (is_exec_mapping(flags))
> +		mm->exec_vm += npages;
> +	else if (is_stack_mapping(flags))
> +		mm->stack_vm += npages;
> +	else if (is_data_mapping(flags))
> +		mm->data_vm += npages;
> +}
> +
> +#undef is_exec_mapping
> +#undef is_stack_mapping
> +#undef is_data_mapping
> +
> +static inline void vm_unacct_memory(long pages)
> +{
> +	vm_acct_memory(-pages);
> +}
> +
> +static inline void mapping_allow_writable(struct address_space *mapping)
> +{
> +	atomic_inc(&mapping->i_mmap_writable);
> +}
> +
> +static inline
> +struct vm_area_struct *vma_find(struct vma_iterator *vmi, unsigned long max)
> +{
> +	return mas_find(&vmi->mas, max - 1);
> +}
> +
> +static inline int vma_iter_clear_gfp(struct vma_iterator *vmi,
> +			unsigned long start, unsigned long end, gfp_t gfp)
> +{
> +	__mas_set_range(&vmi->mas, start, end - 1);
> +	mas_store_gfp(&vmi->mas, NULL, gfp);
> +	if (unlikely(mas_is_err(&vmi->mas)))
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static inline void vma_set_anonymous(struct vm_area_struct *vma)
> +{
> +	vma->vm_ops = NULL;
> +}
> +
> +/* Declared in vma.h. */
> +static inline void set_vma_from_desc(struct vm_area_struct *vma,
> +		struct vm_area_desc *desc);
> +
> +static inline int __compat_vma_mmap(const struct file_operations *f_op,
> +		struct file *file, struct vm_area_struct *vma)
> +{
> +	struct vm_area_desc desc = {
> +		.mm = vma->vm_mm,
> +		.file = file,
> +		.start = vma->vm_start,
> +		.end = vma->vm_end,
> +
> +		.pgoff = vma->vm_pgoff,
> +		.vm_file = vma->vm_file,
> +		.vm_flags = vma->vm_flags,
> +		.page_prot = vma->vm_page_prot,
> +
> +		.action.type = MMAP_NOTHING, /* Default */
> +	};
> +	int err;
> +
> +	err = f_op->mmap_prepare(&desc);
> +	if (err)
> +		return err;
> +
> +	mmap_action_prepare(&desc.action, &desc);
> +	set_vma_from_desc(vma, &desc);
> +	return mmap_action_complete(&desc.action, vma);
> +}
> +
> +static inline int compat_vma_mmap(struct file *file,
> +		struct vm_area_struct *vma)
> +{
> +	return __compat_vma_mmap(file->f_op, file, vma);
> +}
> +
> +
> +static inline void vma_iter_init(struct vma_iterator *vmi,
> +		struct mm_struct *mm, unsigned long addr)
> +{
> +	mas_init(&vmi->mas, &mm->mm_mt, addr);
> +}
> +
> +static inline unsigned long vma_pages(struct vm_area_struct *vma)
> +{
> +	return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> +}
> +
> +static inline void mmap_assert_locked(struct mm_struct *);
> +static inline struct vm_area_struct *find_vma_intersection(struct mm_struct *mm,
> +						unsigned long start_addr,
> +						unsigned long end_addr)
> +{
> +	unsigned long index = start_addr;
> +
> +	mmap_assert_locked(mm);
> +	return mt_find(&mm->mm_mt, &index, end_addr - 1);
> +}
> +
> +static inline
> +struct vm_area_struct *vma_lookup(struct mm_struct *mm, unsigned long addr)
> +{
> +	return mtree_load(&mm->mm_mt, addr);
> +}
> +
> +static inline struct vm_area_struct *vma_prev(struct vma_iterator *vmi)
> +{
> +	return mas_prev(&vmi->mas, 0);
> +}
> +
> +static inline void vma_iter_set(struct vma_iterator *vmi, unsigned long addr)
> +{
> +	mas_set(&vmi->mas, addr);
> +}
> +
> +static inline bool vma_is_anonymous(struct vm_area_struct *vma)
> +{
> +	return !vma->vm_ops;
> +}
> +
> +/* Defined in vma.h, so temporarily define here to avoid circular dependency. */
> +#define vma_iter_load(vmi) \
> +	mas_walk(&(vmi)->mas)
> +
> +static inline struct vm_area_struct *
> +find_vma_prev(struct mm_struct *mm, unsigned long addr,
> +			struct vm_area_struct **pprev)
> +{
> +	struct vm_area_struct *vma;
> +	VMA_ITERATOR(vmi, mm, addr);
> +
> +	vma = vma_iter_load(&vmi);
> +	*pprev = vma_prev(&vmi);
> +	if (!vma)
> +		vma = vma_next(&vmi);
> +	return vma;
> +}
> +
> +#undef vma_iter_load
> +
> +static inline void vma_iter_free(struct vma_iterator *vmi)
> +{
> +	mas_destroy(&vmi->mas);
> +}
> +
> +static inline
> +struct vm_area_struct *vma_iter_next_range(struct vma_iterator *vmi)
> +{
> +	return mas_next_range(&vmi->mas, ULONG_MAX);
> +}
> +
> +bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot);
> +
> +/* Update vma->vm_page_prot to reflect vma->vm_flags. */
> +static inline void vma_set_page_prot(struct vm_area_struct *vma)
> +{
> +	vm_flags_t vm_flags = vma->vm_flags;
> +	pgprot_t vm_page_prot;
> +
> +	/* testing: we inline vm_pgprot_modify() to avoid clash with vma.h. */
> +	vm_page_prot = pgprot_modify(vma->vm_page_prot, vm_get_page_prot(vm_flags));
> +
> +	if (vma_wants_writenotify(vma, vm_page_prot)) {
> +		vm_flags &= ~VM_SHARED;
> +		/* testing: we inline vm_pgprot_modify() to avoid clash with vma.h. */
> +		vm_page_prot = pgprot_modify(vm_page_prot, vm_get_page_prot(vm_flags));
> +	}
> +	/* remove_protection_ptes reads vma->vm_page_prot without mmap_lock */
> +	WRITE_ONCE(vma->vm_page_prot, vm_page_prot);
> +}
> +
> +static inline unsigned long stack_guard_start_gap(struct vm_area_struct *vma)
> +{
> +	if (vma->vm_flags & VM_GROWSDOWN)
> +		return stack_guard_gap;
> +
> +	/* See reasoning around the VM_SHADOW_STACK definition */
> +	if (vma->vm_flags & VM_SHADOW_STACK)
> +		return PAGE_SIZE;
> +
> +	return 0;
> +}
> +
> +static inline unsigned long vm_start_gap(struct vm_area_struct *vma)
> +{
> +	unsigned long gap = stack_guard_start_gap(vma);
> +	unsigned long vm_start = vma->vm_start;
> +
> +	vm_start -= gap;
> +	if (vm_start > vma->vm_start)
> +		vm_start = 0;
> +	return vm_start;
> +}
> +
> +static inline unsigned long vm_end_gap(struct vm_area_struct *vma)
> +{
> +	unsigned long vm_end = vma->vm_end;
> +
> +	if (vma->vm_flags & VM_GROWSUP) {
> +		vm_end += stack_guard_gap;
> +		if (vm_end < vma->vm_end)
> +			vm_end = -PAGE_SIZE;
> +	}
> +	return vm_end;
> +}
> +
> +static inline bool vma_is_accessible(struct vm_area_struct *vma)
> +{
> +	return vma->vm_flags & VM_ACCESS_FLAGS;
> +}
> +
> +static inline bool mlock_future_ok(const struct mm_struct *mm,
> +		vm_flags_t vm_flags, unsigned long bytes)
> +{
> +	unsigned long locked_pages, limit_pages;
> +
> +	if (!(vm_flags & VM_LOCKED) || capable(CAP_IPC_LOCK))
> +		return true;
> +
> +	locked_pages = bytes >> PAGE_SHIFT;
> +	locked_pages += mm->locked_vm;
> +
> +	limit_pages = rlimit(RLIMIT_MEMLOCK);
> +	limit_pages >>= PAGE_SHIFT;
> +
> +	return locked_pages <= limit_pages;
> +}
> +
> +static inline bool map_deny_write_exec(unsigned long old, unsigned long new)
> +{
> +	/* If MDWE is disabled, we have nothing to deny. */
> +	if (mm_flags_test(MMF_HAS_MDWE, current->mm))
> +		return false;
> +
> +	/* If the new VMA is not executable, we have nothing to deny. */
> +	if (!(new & VM_EXEC))
> +		return false;
> +
> +	/* Under MDWE we do not accept newly writably executable VMAs... */
> +	if (new & VM_WRITE)
> +		return true;
> +
> +	/* ...nor previously non-executable VMAs becoming executable. */
> +	if (!(old & VM_EXEC))
> +		return true;
> +
> +	return false;
> +}
> +
> +static inline int mapping_map_writable(struct address_space *mapping)
> +{
> +	return atomic_inc_unless_negative(&mapping->i_mmap_writable) ?
> +		0 : -EPERM;
> +}
> +
> +/* Did the driver provide valid mmap hook configuration? */
> +static inline bool can_mmap_file(struct file *file)
> +{
> +	bool has_mmap = file->f_op->mmap;
> +	bool has_mmap_prepare = file->f_op->mmap_prepare;
> +
> +	/* Hooks are mutually exclusive. */
> +	if (WARN_ON_ONCE(has_mmap && has_mmap_prepare))
> +		return false;
> +	if (!has_mmap && !has_mmap_prepare)
> +		return false;
> +
> +	return true;
> +}
> +
> +static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	if (file->f_op->mmap_prepare)
> +		return compat_vma_mmap(file, vma);
> +
> +	return file->f_op->mmap(file, vma);
> +}
> +
> +static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc)
> +{
> +	return file->f_op->mmap_prepare(desc);
> +}
> +
> +static inline void vma_set_file(struct vm_area_struct *vma, struct file *file)
> +{
> +	/* Changing an anonymous vma with this is illegal */
> +	get_file(file);
> +	swap(vma->vm_file, file);
> +	fput(file);
> +}
> +
> +struct unmap_desc {
> +	struct  ma_state *mas;        /* the maple state point to the first vma */
> +	struct vm_area_struct *first; /* The first vma */
> +	unsigned long pg_start;       /* The first pagetable address to free (floor) */
> +	unsigned long pg_end;         /* The last pagetable address to free (ceiling) */
> +	unsigned long vma_start;      /* The min vma address */
> +	unsigned long vma_end;        /* The max vma address */
> +	unsigned long tree_end;       /* Maximum for the vma tree search */
> +	unsigned long tree_reset;     /* Where to reset the vma tree walk */
> +	bool mm_wr_locked;            /* If the mmap write lock is held */
> +};
> diff --git a/tools/testing/vma/include/stubs.h b/tools/testing/vma/include/stubs.h
> new file mode 100644
> index 000000000000..947a3a0c2566
> --- /dev/null
> +++ b/tools/testing/vma/include/stubs.h
> @@ -0,0 +1,428 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +
> +#pragma once
> +
> +/*
> + * Contains declarations that are STUBBED, that is that are rendered no-ops, in
> + * order to faciliate userland VMA testing.
> + */
> +
> +/* Forward declarations. */
> +struct mm_struct;
> +struct vm_area_struct;
> +struct vm_area_desc;
> +struct pagetable_move_control;
> +struct mmap_action;
> +struct file;
> +struct anon_vma;
> +struct anon_vma_chain;
> +struct address_space;
> +struct unmap_desc;
> +
> +#define __bitwise
> +#define __randomize_layout
> +
> +#define FIRST_USER_ADDRESS	0UL
> +#define USER_PGTABLES_CEILING	0UL
> +
> +#define vma_policy(vma) NULL
> +
> +#define down_write_nest_lock(sem, nest_lock)
> +
> +#define data_race(expr) expr
> +
> +#define ASSERT_EXCLUSIVE_WRITER(x)
> +
> +struct vm_userfaultfd_ctx {};
> +struct mempolicy {};
> +struct mmu_gather {};
> +struct mutex {};
> +struct vm_fault {};
> +
> +static inline void userfaultfd_unmap_complete(struct mm_struct *mm,
> +					      struct list_head *uf)
> +{
> +}
> +
> +static inline unsigned long move_page_tables(struct pagetable_move_control *pmc)
> +{
> +	return 0;
> +}
> +
> +static inline void free_pgd_range(struct mmu_gather *tlb,
> +			unsigned long addr, unsigned long end,
> +			unsigned long floor, unsigned long ceiling)
> +{
> +}
> +
> +static inline int ksm_execve(struct mm_struct *mm)
> +{
> +	return 0;
> +}
> +
> +static inline void ksm_exit(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void vma_numab_state_init(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void vma_numab_state_free(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void dup_anon_vma_name(struct vm_area_struct *orig_vma,
> +				     struct vm_area_struct *new_vma)
> +{
> +}
> +
> +static inline void free_anon_vma_name(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void mmap_action_prepare(struct mmap_action *action,
> +					   struct vm_area_desc *desc)
> +{
> +}
> +
> +static inline int mmap_action_complete(struct mmap_action *action,
> +					   struct vm_area_struct *vma)
> +{
> +	return 0;
> +}
> +
> +static inline void fixup_hugetlb_reservations(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline bool shmem_file(struct file *file)
> +{
> +	return false;
> +}
> +
> +static inline vm_flags_t ksm_vma_flags(const struct mm_struct *mm,
> +		const struct file *file, vm_flags_t vm_flags)
> +{
> +	return vm_flags;
> +}
> +
> +static inline void remap_pfn_range_prepare(struct vm_area_desc *desc, unsigned long pfn)
> +{
> +}
> +
> +static inline int remap_pfn_range_complete(struct vm_area_struct *vma, unsigned long addr,
> +		unsigned long pfn, unsigned long size, pgprot_t pgprot)
> +{
> +	return 0;
> +}
> +
> +static inline int do_munmap(struct mm_struct *, unsigned long, size_t,
> +		struct list_head *uf)
> +{
> +	return 0;
> +}
> +
> +/* Currently stubbed but we may later wish to un-stub. */
> +static inline void vm_acct_memory(long pages);
> +
> +static inline void mmap_assert_locked(struct mm_struct *mm)
> +{
> +}
> +
> +
> +static inline void anon_vma_unlock_write(struct anon_vma *anon_vma)
> +{
> +}
> +
> +static inline void i_mmap_unlock_write(struct address_space *mapping)
> +{
> +}
> +
> +static inline int userfaultfd_unmap_prep(struct vm_area_struct *vma,
> +					 unsigned long start,
> +					 unsigned long end,
> +					 struct list_head *unmaps)
> +{
> +	return 0;
> +}
> +
> +static inline void mmap_write_downgrade(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void mmap_read_unlock(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void mmap_write_unlock(struct mm_struct *mm)
> +{
> +}
> +
> +static inline int mmap_write_lock_killable(struct mm_struct *mm)
> +{
> +	return 0;
> +}
> +
> +static inline bool can_modify_mm(struct mm_struct *mm,
> +				 unsigned long start,
> +				 unsigned long end)
> +{
> +	return true;
> +}
> +
> +static inline void arch_unmap(struct mm_struct *mm,
> +				 unsigned long start,
> +				 unsigned long end)
> +{
> +}
> +
> +static inline bool mpol_equal(struct mempolicy *a, struct mempolicy *b)
> +{
> +	return true;
> +}
> +
> +static inline void khugepaged_enter_vma(struct vm_area_struct *vma,
> +			  vm_flags_t vm_flags)
> +{
> +}
> +
> +static inline bool mapping_can_writeback(struct address_space *mapping)
> +{
> +	return true;
> +}
> +
> +static inline bool is_vm_hugetlb_page(struct vm_area_struct *vma)
> +{
> +	return false;
> +}
> +
> +static inline bool vma_soft_dirty_enabled(struct vm_area_struct *vma)
> +{
> +	return false;
> +}
> +
> +static inline bool userfaultfd_wp(struct vm_area_struct *vma)
> +{
> +	return false;
> +}
> +
> +static inline void mmap_assert_write_locked(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void mutex_lock(struct mutex *lock)
> +{
> +}
> +
> +static inline void mutex_unlock(struct mutex *lock)
> +{
> +}
> +
> +static inline bool mutex_is_locked(struct mutex *lock)
> +{
> +	return true;
> +}
> +
> +static inline bool signal_pending(void *p)
> +{
> +	return false;
> +}
> +
> +static inline bool is_file_hugepages(struct file *file)
> +{
> +	return false;
> +}
> +
> +static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
> +{
> +	return 0;
> +}
> +
> +static inline bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags,
> +				 unsigned long npages)
> +{
> +	return true;
> +}
> +
> +static inline int shmem_zero_setup(struct vm_area_struct *vma)
> +{
> +	return 0;
> +}
> +
> +
> +static inline void vm_acct_memory(long pages)
> +{
> +}
> +
> +static inline void vma_interval_tree_insert(struct vm_area_struct *vma,
> +					    struct rb_root_cached *rb)
> +{
> +}
> +
> +static inline void vma_interval_tree_remove(struct vm_area_struct *vma,
> +					    struct rb_root_cached *rb)
> +{
> +}
> +
> +static inline void flush_dcache_mmap_unlock(struct address_space *mapping)
> +{
> +}
> +
> +static inline void anon_vma_interval_tree_insert(struct anon_vma_chain *avc,
> +						 struct rb_root_cached *rb)
> +{
> +}
> +
> +static inline void anon_vma_interval_tree_remove(struct anon_vma_chain *avc,
> +						 struct rb_root_cached *rb)
> +{
> +}
> +
> +static inline void uprobe_mmap(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void uprobe_munmap(struct vm_area_struct *vma,
> +				 unsigned long start, unsigned long end)
> +{
> +}
> +
> +static inline void i_mmap_lock_write(struct address_space *mapping)
> +{
> +}
> +
> +static inline void anon_vma_lock_write(struct anon_vma *anon_vma)
> +{
> +}
> +
> +static inline void vma_assert_write_locked(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void ksm_add_vma(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline void perf_event_mmap(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline bool vma_is_dax(struct vm_area_struct *vma)
> +{
> +	return false;
> +}
> +
> +static inline struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
> +{
> +	return NULL;
> +}
> +
> +static inline bool arch_validate_flags(vm_flags_t flags)
> +{
> +	return true;
> +}
> +
> +static inline void vma_close(struct vm_area_struct *vma)
> +{
> +}
> +
> +static inline int mmap_file(struct file *file, struct vm_area_struct *vma)
> +{
> +	return 0;
> +}
> +
> +static inline int is_hugepage_only_range(struct mm_struct *mm,
> +					unsigned long addr, unsigned long len)
> +{
> +	return 0;
> +}
> +
> +static inline bool capable(int cap)
> +{
> +	return true;
> +}
> +
> +static inline struct anon_vma_name *anon_vma_name(struct vm_area_struct *vma)
> +{
> +	return NULL;
> +}
> +
> +static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma,
> +					struct vm_userfaultfd_ctx vm_ctx)
> +{
> +	return true;
> +}
> +
> +static inline bool anon_vma_name_eq(struct anon_vma_name *anon_name1,
> +				    struct anon_vma_name *anon_name2)
> +{
> +	return true;
> +}
> +
> +static inline void might_sleep(void)
> +{
> +}
> +
> +static inline void fput(struct file *file)
> +{
> +}
> +
> +static inline void mpol_put(struct mempolicy *pol)
> +{
> +}
> +
> +static inline void lru_add_drain(void)
> +{
> +}
> +
> +static inline void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm)
> +{
> +}
> +
> +static inline void update_hiwater_rss(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void update_hiwater_vm(struct mm_struct *mm)
> +{
> +}
> +
> +static inline void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap)
> +{
> +}
> +
> +static inline void free_pgtables(struct mmu_gather *tlb, struct unmap_desc *unmap)
> +{
> +}
> +
> +static inline void mapping_unmap_writable(struct address_space *mapping)
> +{
> +}
> +
> +static inline void flush_dcache_mmap_lock(struct address_space *mapping)
> +{
> +}
> +
> +static inline void tlb_finish_mmu(struct mmu_gather *tlb)
> +{
> +}
> +
> +static inline struct file *get_file(struct file *f)
> +{
> +	return f;
> +}
> +
> +static inline int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst)
> +{
> +	return 0;
> +}
> +
> +static inline void vma_adjust_trans_huge(struct vm_area_struct *vma,
> +					 unsigned long start,
> +					 unsigned long end,
> +					 struct vm_area_struct *next)
> +{
> +}
> +
> +static inline void hugetlb_split(struct vm_area_struct *, unsigned long) {}
> diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
> index b48ebae3927d..e3ed05b57819 100644
> --- a/tools/testing/vma/vma_internal.h
> +++ b/tools/testing/vma/vma_internal.h
> @@ -12,15 +12,11 @@
>  #ifndef __MM_VMA_INTERNAL_H
>  #define __MM_VMA_INTERNAL_H
>  
> -#define __private
> -#define __bitwise
> -#define __randomize_layout
> +#include <stdlib.h>
>  
>  #define CONFIG_MMU
>  #define CONFIG_PER_VMA_LOCK
>  
> -#include <stdlib.h>
> -
>  #ifdef __CONCAT
>  #undef __CONCAT
>  #endif
> @@ -35,1936 +31,28 @@
>  #include <linux/refcount.h>
>  #include <linux/slab.h>
>  
> -extern unsigned long stack_guard_gap;
> -#ifdef CONFIG_MMU
> -extern unsigned long mmap_min_addr;
> -extern unsigned long dac_mmap_min_addr;
> -#else
> -#define mmap_min_addr		0UL
> -#define dac_mmap_min_addr	0UL
> -#endif
> -
> -#define ACCESS_PRIVATE(p, member) ((p)->member)
> -
> -#define VM_WARN_ON(_expr) (WARN_ON(_expr))
> -#define VM_WARN_ON_ONCE(_expr) (WARN_ON_ONCE(_expr))
> -#define VM_WARN_ON_VMG(_expr, _vmg) (WARN_ON(_expr))
> -#define VM_BUG_ON(_expr) (BUG_ON(_expr))
> -#define VM_BUG_ON_VMA(_expr, _vma) (BUG_ON(_expr))
> -
> -#define MMF_HAS_MDWE	28
> -
> -/*
> - * vm_flags in vm_area_struct, see mm_types.h.
> - * When changing, update also include/trace/events/mmflags.h
> - */
> -
> -#define VM_NONE		0x00000000
> -
> -/**
> - * typedef vma_flag_t - specifies an individual VMA flag by bit number.
> - *
> - * This value is made type safe by sparse to avoid passing invalid flag values
> - * around.
> - */
> -typedef int __bitwise vma_flag_t;
> -
> -#define DECLARE_VMA_BIT(name, bitnum) \
> -	VMA_ ## name ## _BIT = ((__force vma_flag_t)bitnum)
> -#define DECLARE_VMA_BIT_ALIAS(name, aliased) \
> -	VMA_ ## name ## _BIT = VMA_ ## aliased ## _BIT
> -enum {
> -	DECLARE_VMA_BIT(READ, 0),
> -	DECLARE_VMA_BIT(WRITE, 1),
> -	DECLARE_VMA_BIT(EXEC, 2),
> -	DECLARE_VMA_BIT(SHARED, 3),
> -	/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */
> -	DECLARE_VMA_BIT(MAYREAD, 4),	/* limits for mprotect() etc. */
> -	DECLARE_VMA_BIT(MAYWRITE, 5),
> -	DECLARE_VMA_BIT(MAYEXEC, 6),
> -	DECLARE_VMA_BIT(MAYSHARE, 7),
> -	DECLARE_VMA_BIT(GROWSDOWN, 8),	/* general info on the segment */
> -#ifdef CONFIG_MMU
> -	DECLARE_VMA_BIT(UFFD_MISSING, 9),/* missing pages tracking */
> -#else
> -	/* nommu: R/O MAP_PRIVATE mapping that might overlay a file mapping */
> -	DECLARE_VMA_BIT(MAYOVERLAY, 9),
> -#endif /* CONFIG_MMU */
> -	/* Page-ranges managed without "struct page", just pure PFN */
> -	DECLARE_VMA_BIT(PFNMAP, 10),
> -	DECLARE_VMA_BIT(MAYBE_GUARD, 11),
> -	DECLARE_VMA_BIT(UFFD_WP, 12),	/* wrprotect pages tracking */
> -	DECLARE_VMA_BIT(LOCKED, 13),
> -	DECLARE_VMA_BIT(IO, 14),	/* Memory mapped I/O or similar */
> -	DECLARE_VMA_BIT(SEQ_READ, 15),	/* App will access data sequentially */
> -	DECLARE_VMA_BIT(RAND_READ, 16),	/* App will not benefit from clustered reads */
> -	DECLARE_VMA_BIT(DONTCOPY, 17),	/* Do not copy this vma on fork */
> -	DECLARE_VMA_BIT(DONTEXPAND, 18),/* Cannot expand with mremap() */
> -	DECLARE_VMA_BIT(LOCKONFAULT, 19),/* Lock pages covered when faulted in */
> -	DECLARE_VMA_BIT(ACCOUNT, 20),	/* Is a VM accounted object */
> -	DECLARE_VMA_BIT(NORESERVE, 21),	/* should the VM suppress accounting */
> -	DECLARE_VMA_BIT(HUGETLB, 22),	/* Huge TLB Page VM */
> -	DECLARE_VMA_BIT(SYNC, 23),	/* Synchronous page faults */
> -	DECLARE_VMA_BIT(ARCH_1, 24),	/* Architecture-specific flag */
> -	DECLARE_VMA_BIT(WIPEONFORK, 25),/* Wipe VMA contents in child. */
> -	DECLARE_VMA_BIT(DONTDUMP, 26),	/* Do not include in the core dump */
> -	DECLARE_VMA_BIT(SOFTDIRTY, 27),	/* NOT soft dirty clean area */
> -	DECLARE_VMA_BIT(MIXEDMAP, 28),	/* Can contain struct page and pure PFN pages */
> -	DECLARE_VMA_BIT(HUGEPAGE, 29),	/* MADV_HUGEPAGE marked this vma */
> -	DECLARE_VMA_BIT(NOHUGEPAGE, 30),/* MADV_NOHUGEPAGE marked this vma */
> -	DECLARE_VMA_BIT(MERGEABLE, 31),	/* KSM may merge identical pages */
> -	/* These bits are reused, we define specific uses below. */
> -	DECLARE_VMA_BIT(HIGH_ARCH_0, 32),
> -	DECLARE_VMA_BIT(HIGH_ARCH_1, 33),
> -	DECLARE_VMA_BIT(HIGH_ARCH_2, 34),
> -	DECLARE_VMA_BIT(HIGH_ARCH_3, 35),
> -	DECLARE_VMA_BIT(HIGH_ARCH_4, 36),
> -	DECLARE_VMA_BIT(HIGH_ARCH_5, 37),
> -	DECLARE_VMA_BIT(HIGH_ARCH_6, 38),
> -	/*
> -	 * This flag is used to connect VFIO to arch specific KVM code. It
> -	 * indicates that the memory under this VMA is safe for use with any
> -	 * non-cachable memory type inside KVM. Some VFIO devices, on some
> -	 * platforms, are thought to be unsafe and can cause machine crashes
> -	 * if KVM does not lock down the memory type.
> -	 */
> -	DECLARE_VMA_BIT(ALLOW_ANY_UNCACHED, 39),
> -#ifdef CONFIG_PPC32
> -	DECLARE_VMA_BIT_ALIAS(DROPPABLE, ARCH_1),
> -#else
> -	DECLARE_VMA_BIT(DROPPABLE, 40),
> -#endif
> -	DECLARE_VMA_BIT(UFFD_MINOR, 41),
> -	DECLARE_VMA_BIT(SEALED, 42),
> -	/* Flags that reuse flags above. */
> -	DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),
> -	DECLARE_VMA_BIT_ALIAS(PKEY_BIT1, HIGH_ARCH_1),
> -	DECLARE_VMA_BIT_ALIAS(PKEY_BIT2, HIGH_ARCH_2),
> -	DECLARE_VMA_BIT_ALIAS(PKEY_BIT3, HIGH_ARCH_3),
> -	DECLARE_VMA_BIT_ALIAS(PKEY_BIT4, HIGH_ARCH_4),
> -#if defined(CONFIG_X86_USER_SHADOW_STACK)
> -	/*
> -	 * VM_SHADOW_STACK should not be set with VM_SHARED because of lack of
> -	 * support core mm.
> -	 *
> -	 * These VMAs will get a single end guard page. This helps userspace
> -	 * protect itself from attacks. A single page is enough for current
> -	 * shadow stack archs (x86). See the comments near alloc_shstk() in
> -	 * arch/x86/kernel/shstk.c for more details on the guard size.
> -	 */
> -	DECLARE_VMA_BIT_ALIAS(SHADOW_STACK, HIGH_ARCH_5),
> -#elif defined(CONFIG_ARM64_GCS)
> -	/*
> -	 * arm64's Guarded Control Stack implements similar functionality and
> -	 * has similar constraints to shadow stacks.
> -	 */
> -	DECLARE_VMA_BIT_ALIAS(SHADOW_STACK, HIGH_ARCH_6),
> -#endif
> -	DECLARE_VMA_BIT_ALIAS(SAO, ARCH_1),		/* Strong Access Ordering (powerpc) */
> -	DECLARE_VMA_BIT_ALIAS(GROWSUP, ARCH_1),		/* parisc */
> -	DECLARE_VMA_BIT_ALIAS(SPARC_ADI, ARCH_1),	/* sparc64 */
> -	DECLARE_VMA_BIT_ALIAS(ARM64_BTI, ARCH_1),	/* arm64 */
> -	DECLARE_VMA_BIT_ALIAS(ARCH_CLEAR, ARCH_1),	/* sparc64, arm64 */
> -	DECLARE_VMA_BIT_ALIAS(MAPPED_COPY, ARCH_1),	/* !CONFIG_MMU */
> -	DECLARE_VMA_BIT_ALIAS(MTE, HIGH_ARCH_4),	/* arm64 */
> -	DECLARE_VMA_BIT_ALIAS(MTE_ALLOWED, HIGH_ARCH_5),/* arm64 */
> -#ifdef CONFIG_STACK_GROWSUP
> -	DECLARE_VMA_BIT_ALIAS(STACK, GROWSUP),
> -	DECLARE_VMA_BIT_ALIAS(STACK_EARLY, GROWSDOWN),
> -#else
> -	DECLARE_VMA_BIT_ALIAS(STACK, GROWSDOWN),
> -#endif
> -};
> -
> -#define INIT_VM_FLAG(name) BIT((__force int) VMA_ ## name ## _BIT)
> -#define VM_READ		INIT_VM_FLAG(READ)
> -#define VM_WRITE	INIT_VM_FLAG(WRITE)
> -#define VM_EXEC		INIT_VM_FLAG(EXEC)
> -#define VM_SHARED	INIT_VM_FLAG(SHARED)
> -#define VM_MAYREAD	INIT_VM_FLAG(MAYREAD)
> -#define VM_MAYWRITE	INIT_VM_FLAG(MAYWRITE)
> -#define VM_MAYEXEC	INIT_VM_FLAG(MAYEXEC)
> -#define VM_MAYSHARE	INIT_VM_FLAG(MAYSHARE)
> -#define VM_GROWSDOWN	INIT_VM_FLAG(GROWSDOWN)
> -#ifdef CONFIG_MMU
> -#define VM_UFFD_MISSING	INIT_VM_FLAG(UFFD_MISSING)
> -#else
> -#define VM_UFFD_MISSING	VM_NONE
> -#define VM_MAYOVERLAY	INIT_VM_FLAG(MAYOVERLAY)
> -#endif
> -#define VM_PFNMAP	INIT_VM_FLAG(PFNMAP)
> -#define VM_MAYBE_GUARD	INIT_VM_FLAG(MAYBE_GUARD)
> -#define VM_UFFD_WP	INIT_VM_FLAG(UFFD_WP)
> -#define VM_LOCKED	INIT_VM_FLAG(LOCKED)
> -#define VM_IO		INIT_VM_FLAG(IO)
> -#define VM_SEQ_READ	INIT_VM_FLAG(SEQ_READ)
> -#define VM_RAND_READ	INIT_VM_FLAG(RAND_READ)
> -#define VM_DONTCOPY	INIT_VM_FLAG(DONTCOPY)
> -#define VM_DONTEXPAND	INIT_VM_FLAG(DONTEXPAND)
> -#define VM_LOCKONFAULT	INIT_VM_FLAG(LOCKONFAULT)
> -#define VM_ACCOUNT	INIT_VM_FLAG(ACCOUNT)
> -#define VM_NORESERVE	INIT_VM_FLAG(NORESERVE)
> -#define VM_HUGETLB	INIT_VM_FLAG(HUGETLB)
> -#define VM_SYNC		INIT_VM_FLAG(SYNC)
> -#define VM_ARCH_1	INIT_VM_FLAG(ARCH_1)
> -#define VM_WIPEONFORK	INIT_VM_FLAG(WIPEONFORK)
> -#define VM_DONTDUMP	INIT_VM_FLAG(DONTDUMP)
> -#ifdef CONFIG_MEM_SOFT_DIRTY
> -#define VM_SOFTDIRTY	INIT_VM_FLAG(SOFTDIRTY)
> -#else
> -#define VM_SOFTDIRTY	VM_NONE
> -#endif
> -#define VM_MIXEDMAP	INIT_VM_FLAG(MIXEDMAP)
> -#define VM_HUGEPAGE	INIT_VM_FLAG(HUGEPAGE)
> -#define VM_NOHUGEPAGE	INIT_VM_FLAG(NOHUGEPAGE)
> -#define VM_MERGEABLE	INIT_VM_FLAG(MERGEABLE)
> -#define VM_STACK	INIT_VM_FLAG(STACK)
> -#ifdef CONFIG_STACK_GROWS_UP
> -#define VM_STACK_EARLY	INIT_VM_FLAG(STACK_EARLY)
> -#else
> -#define VM_STACK_EARLY	VM_NONE
> -#endif
> -#ifdef CONFIG_ARCH_HAS_PKEYS
> -#define VM_PKEY_SHIFT ((__force int)VMA_HIGH_ARCH_0_BIT)
> -/* Despite the naming, these are FLAGS not bits. */
> -#define VM_PKEY_BIT0 INIT_VM_FLAG(PKEY_BIT0)
> -#define VM_PKEY_BIT1 INIT_VM_FLAG(PKEY_BIT1)
> -#define VM_PKEY_BIT2 INIT_VM_FLAG(PKEY_BIT2)
> -#if CONFIG_ARCH_PKEY_BITS > 3
> -#define VM_PKEY_BIT3 INIT_VM_FLAG(PKEY_BIT3)
> -#else
> -#define VM_PKEY_BIT3  VM_NONE
> -#endif /* CONFIG_ARCH_PKEY_BITS > 3 */
> -#if CONFIG_ARCH_PKEY_BITS > 4
> -#define VM_PKEY_BIT4 INIT_VM_FLAG(PKEY_BIT4)
> -#else
> -#define VM_PKEY_BIT4  VM_NONE
> -#endif /* CONFIG_ARCH_PKEY_BITS > 4 */
> -#endif /* CONFIG_ARCH_HAS_PKEYS */
> -#if defined(CONFIG_X86_USER_SHADOW_STACK) || defined(CONFIG_ARM64_GCS)
> -#define VM_SHADOW_STACK	INIT_VM_FLAG(SHADOW_STACK)
> -#else
> -#define VM_SHADOW_STACK	VM_NONE
> -#endif
> -#if defined(CONFIG_PPC64)
> -#define VM_SAO		INIT_VM_FLAG(SAO)
> -#elif defined(CONFIG_PARISC)
> -#define VM_GROWSUP	INIT_VM_FLAG(GROWSUP)
> -#elif defined(CONFIG_SPARC64)
> -#define VM_SPARC_ADI	INIT_VM_FLAG(SPARC_ADI)
> -#define VM_ARCH_CLEAR	INIT_VM_FLAG(ARCH_CLEAR)
> -#elif defined(CONFIG_ARM64)
> -#define VM_ARM64_BTI	INIT_VM_FLAG(ARM64_BTI)
> -#define VM_ARCH_CLEAR	INIT_VM_FLAG(ARCH_CLEAR)
> -#elif !defined(CONFIG_MMU)
> -#define VM_MAPPED_COPY	INIT_VM_FLAG(MAPPED_COPY)
> -#endif
> -#ifndef VM_GROWSUP
> -#define VM_GROWSUP	VM_NONE
> -#endif
> -#ifdef CONFIG_ARM64_MTE
> -#define VM_MTE		INIT_VM_FLAG(MTE)
> -#define VM_MTE_ALLOWED	INIT_VM_FLAG(MTE_ALLOWED)
> -#else
> -#define VM_MTE		VM_NONE
> -#define VM_MTE_ALLOWED	VM_NONE
> -#endif
> -#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
> -#define VM_UFFD_MINOR	INIT_VM_FLAG(UFFD_MINOR)
> -#else
> -#define VM_UFFD_MINOR	VM_NONE
> -#endif
> -#ifdef CONFIG_64BIT
> -#define VM_ALLOW_ANY_UNCACHED	INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
> -#define VM_SEALED		INIT_VM_FLAG(SEALED)
> -#else
> -#define VM_ALLOW_ANY_UNCACHED	VM_NONE
> -#define VM_SEALED		VM_NONE
> -#endif
> -#if defined(CONFIG_64BIT) || defined(CONFIG_PPC32)
> -#define VM_DROPPABLE		INIT_VM_FLAG(DROPPABLE)
> -#else
> -#define VM_DROPPABLE		VM_NONE
> -#endif
> -
> -/* Bits set in the VMA until the stack is in its final location */
> -#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY)
> -
> -#define TASK_EXEC ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0)
> -
> -/* Common data flag combinations */
> -#define VM_DATA_FLAGS_TSK_EXEC	(VM_READ | VM_WRITE | TASK_EXEC | \
> -				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> -#define VM_DATA_FLAGS_NON_EXEC	(VM_READ | VM_WRITE | VM_MAYREAD | \
> -				 VM_MAYWRITE | VM_MAYEXEC)
> -#define VM_DATA_FLAGS_EXEC	(VM_READ | VM_WRITE | VM_EXEC | \
> -				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> -
> -#ifndef VM_DATA_DEFAULT_FLAGS		/* arch can override this */
> -#define VM_DATA_DEFAULT_FLAGS  VM_DATA_FLAGS_EXEC
> -#endif
> -
> -#ifndef VM_STACK_DEFAULT_FLAGS		/* arch can override this */
> -#define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
> -#endif
> -
> -#define VM_STARTGAP_FLAGS (VM_GROWSDOWN | VM_SHADOW_STACK)
> -
> -#define VM_STACK_FLAGS	(VM_STACK | VM_STACK_DEFAULT_FLAGS | VM_ACCOUNT)
> -
> -/* VMA basic access permission flags */
> -#define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC)
> -
> -/*
> - * Special vmas that are non-mergable, non-mlock()able.
> - */
> -#define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_PFNMAP | VM_MIXEDMAP)
> -
> -#define DEFAULT_MAP_WINDOW	((1UL << 47) - PAGE_SIZE)
> -#define TASK_SIZE_LOW		DEFAULT_MAP_WINDOW
> -#define TASK_SIZE_MAX		DEFAULT_MAP_WINDOW
> -#define STACK_TOP		TASK_SIZE_LOW
> -#define STACK_TOP_MAX		TASK_SIZE_MAX
> -
> -/* This mask represents all the VMA flag bits used by mlock */
> -#define VM_LOCKED_MASK	(VM_LOCKED | VM_LOCKONFAULT)
> -
> -#define TASK_EXEC ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0)
> -
> -#define VM_DATA_FLAGS_TSK_EXEC	(VM_READ | VM_WRITE | TASK_EXEC | \
> -				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
> -
> -#define RLIMIT_STACK		3	/* max stack size */
> -#define RLIMIT_MEMLOCK		8	/* max locked-in-memory address space */
> -
> -#define CAP_IPC_LOCK         14
> -
> -/*
> - * Flags which should be 'sticky' on merge - that is, flags which, when one VMA
> - * possesses it but the other does not, the merged VMA should nonetheless have
> - * applied to it:
> - *
> - *   VM_SOFTDIRTY - if a VMA is marked soft-dirty, that is has not had its
> - *                  references cleared via /proc/$pid/clear_refs, any merged VMA
> - *                  should be considered soft-dirty also as it operates at a VMA
> - *                  granularity.
> - */
> -#define VM_STICKY (VM_SOFTDIRTY | VM_MAYBE_GUARD)
> -
>  /*
> - * VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one
> - * of these flags and the other not does not preclude a merge.
> - *
> - *    VM_STICKY - When merging VMAs, VMA flags must match, unless they are
> - *                'sticky'. If any sticky flags exist in either VMA, we simply
> - *                set all of them on the merged VMA.
> + * DUPLICATE typedef definitions from kernel source that have to be declared
> + * ahead of all other headers.
>   */
> -#define VM_IGNORE_MERGE VM_STICKY
> -
> -/*
> - * Flags which should result in page tables being copied on fork. These are
> - * flags which indicate that the VMA maps page tables which cannot be
> - * reconsistuted upon page fault, so necessitate page table copying upon
> - *
> - * VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be
> - *                           reasonably reconstructed on page fault.
> - *
> - *              VM_UFFD_WP - Encodes metadata about an installed uffd
> - *                           write protect handler, which cannot be
> - *                           reconstructed on page fault.
> - *
> - *                           We always copy pgtables when dst_vma has uffd-wp
> - *                           enabled even if it's file-backed
> - *                           (e.g. shmem). Because when uffd-wp is enabled,
> - *                           pgtable contains uffd-wp protection information,
> - *                           that's something we can't retrieve from page cache,
> - *                           and skip copying will lose those info.
> - *
> - *          VM_MAYBE_GUARD - Could contain page guard region markers which
> - *                           by design are a property of the page tables
> - *                           only and thus cannot be reconstructed on page
> - *                           fault.
> - */
> -#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_MAYBE_GUARD)
> -
> -#define FIRST_USER_ADDRESS	0UL
> -#define USER_PGTABLES_CEILING	0UL
> -
> -#define vma_policy(vma) NULL
> -
> -#define down_write_nest_lock(sem, nest_lock)
> -
> -#define pgprot_val(x)		((x).pgprot)
> -#define __pgprot(x)		((pgprot_t) { (x) } )
> -
> -#define for_each_vma(__vmi, __vma)					\
> -	while (((__vma) = vma_next(&(__vmi))) != NULL)
> -
> -/* The MM code likes to work with exclusive end addresses */
> -#define for_each_vma_range(__vmi, __vma, __end)				\
> -	while (((__vma) = vma_find(&(__vmi), (__end))) != NULL)
> -
> -#define offset_in_page(p)	((unsigned long)(p) & ~PAGE_MASK)
> -
> -#define PHYS_PFN(x)	((unsigned long)((x) >> PAGE_SHIFT))
> -
> -#define test_and_set_bit(nr, addr) __test_and_set_bit(nr, addr)
> -#define test_and_clear_bit(nr, addr) __test_and_clear_bit(nr, addr)
> -
> -#define TASK_SIZE ((1ul << 47)-PAGE_SIZE)
> -
> -#define AS_MM_ALL_LOCKS 2
> -
> -/* We hardcode this for now. */
> -#define sysctl_max_map_count 0x1000000UL
> -
> -#define pgoff_t unsigned long
> -typedef unsigned long	pgprotval_t;
> -typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;
> -typedef unsigned long vm_flags_t;
> -typedef __bitwise unsigned int vm_fault_t;
> -
> -/*
> - * The shared stubs do not implement this, it amounts to an fprintf(STDERR,...)
> - * either way :)
> - */
> -#define pr_warn_once pr_err
> -
> -#define data_race(expr) expr
> -
> -#define ASSERT_EXCLUSIVE_WRITER(x)
> -
> -#define pgtable_supports_soft_dirty() 1
> -
> -/**
> - * swap - swap values of @a and @b
> - * @a: first value
> - * @b: second value
> - */
> -#define swap(a, b) \
> -	do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
> -
> -struct kref {
> -	refcount_t refcount;
> -};
> -
> -/*
> - * Define the task command name length as enum, then it can be visible to
> - * BPF programs.
> - */
> -enum {
> -	TASK_COMM_LEN = 16,
> -};
> -
> -/*
> - * Flags for bug emulation.
> - *
> - * These occupy the top three bytes.
> - */
> -enum {
> -	READ_IMPLIES_EXEC =	0x0400000,
> -};
> -
> -struct task_struct {
> -	char comm[TASK_COMM_LEN];
> -	pid_t pid;
> -	struct mm_struct *mm;
> -
> -	/* Used for emulating ABI behavior of previous Linux versions: */
> -	unsigned int			personality;
> -};
> -
> -struct task_struct *get_current(void);
> -#define current get_current()
> -
> -struct anon_vma {
> -	struct anon_vma *root;
> -	struct rb_root_cached rb_root;
> -
> -	/* Test fields. */
> -	bool was_cloned;
> -	bool was_unlinked;
> -};
> -
> -struct anon_vma_chain {
> -	struct anon_vma *anon_vma;
> -	struct list_head same_vma;
> -};
> -
> -struct anon_vma_name {
> -	struct kref kref;
> -	/* The name needs to be at the end because it is dynamically sized. */
> -	char name[];
> -};
> -
> -struct vma_iterator {
> -	struct ma_state mas;
> -};
> -
> -#define VMA_ITERATOR(name, __mm, __addr)				\
> -	struct vma_iterator name = {					\
> -		.mas = {						\
> -			.tree = &(__mm)->mm_mt,				\
> -			.index = __addr,				\
> -			.node = NULL,					\
> -			.status = ma_start,				\
> -		},							\
> -	}
> -
> -struct address_space {
> -	struct rb_root_cached	i_mmap;
> -	unsigned long		flags;
> -	atomic_t		i_mmap_writable;
> -};
> -
> -struct vm_userfaultfd_ctx {};
> -struct mempolicy {};
> -struct mmu_gather {};
> -struct mutex {};
> -#define DEFINE_MUTEX(mutexname) \
> -	struct mutex mutexname = {}
> -
> -#define DECLARE_BITMAP(name, bits) \
> -	unsigned long name[BITS_TO_LONGS(bits)]
> -
> +#define __private
>  #define NUM_MM_FLAG_BITS (64)
>  typedef struct {
>  	__private DECLARE_BITMAP(__mm_flags, NUM_MM_FLAG_BITS);
>  } mm_flags_t;
> -
> -/*
> - * Opaque type representing current VMA (vm_area_struct) flag state. Must be
> - * accessed via vma_flags_xxx() helper functions.
> - */
>  #define NUM_VMA_FLAG_BITS BITS_PER_LONG
>  typedef struct {
>  	DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS);
>  } __private vma_flags_t;
>  
> -#define EMPTY_VMA_FLAGS ((vma_flags_t){ })
> -
> -struct mm_struct {
> -	struct maple_tree mm_mt;
> -	int map_count;			/* number of VMAs */
> -	unsigned long total_vm;	   /* Total pages mapped */
> -	unsigned long locked_vm;   /* Pages that have PG_mlocked set */
> -	unsigned long data_vm;	   /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
> -	unsigned long exec_vm;	   /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
> -	unsigned long stack_vm;	   /* VM_STACK */
> -
> -	unsigned long def_flags;
> -
> -	mm_flags_t flags; /* Must use mm_flags_* helpers to access */
> -};
> -
> -struct vm_area_struct;
> -
> -
> -/* What action should be taken after an .mmap_prepare call is complete? */
> -enum mmap_action_type {
> -	MMAP_NOTHING,		/* Mapping is complete, no further action. */
> -	MMAP_REMAP_PFN,		/* Remap PFN range. */
> -	MMAP_IO_REMAP_PFN,	/* I/O remap PFN range. */
> -};
> -
> -/*
> - * Describes an action an mmap_prepare hook can instruct to be taken to complete
> - * the mapping of a VMA. Specified in vm_area_desc.
> - */
> -struct mmap_action {
> -	union {
> -		/* Remap range. */
> -		struct {
> -			unsigned long start;
> -			unsigned long start_pfn;
> -			unsigned long size;
> -			pgprot_t pgprot;
> -		} remap;
> -	};
> -	enum mmap_action_type type;
> -
> -	/*
> -	 * If specified, this hook is invoked after the selected action has been
> -	 * successfully completed. Note that the VMA write lock still held.
> -	 *
> -	 * The absolute minimum ought to be done here.
> -	 *
> -	 * Returns 0 on success, or an error code.
> -	 */
> -	int (*success_hook)(const struct vm_area_struct *vma);
> -
> -	/*
> -	 * If specified, this hook is invoked when an error occurred when
> -	 * attempting the selection action.
> -	 *
> -	 * The hook can return an error code in order to filter the error, but
> -	 * it is not valid to clear the error here.
> -	 */
> -	int (*error_hook)(int err);
> -
> -	/*
> -	 * This should be set in rare instances where the operation required
> -	 * that the rmap should not be able to access the VMA until
> -	 * completely set up.
> -	 */
> -	bool hide_from_rmap_until_complete :1;
> -};
> -
> -/* Operations which modify VMAs. */
> -enum vma_operation {
> -	VMA_OP_SPLIT,
> -	VMA_OP_MERGE_UNFAULTED,
> -	VMA_OP_REMAP,
> -	VMA_OP_FORK,
> -};
> -
> -/*
> - * Describes a VMA that is about to be mmap()'ed. Drivers may choose to
> - * manipulate mutable fields which will cause those fields to be updated in the
> - * resultant VMA.
> - *
> - * Helper functions are not required for manipulating any field.
> - */
> -struct vm_area_desc {
> -	/* Immutable state. */
> -	const struct mm_struct *const mm;
> -	struct file *const file; /* May vary from vm_file in stacked callers. */
> -	unsigned long start;
> -	unsigned long end;
> -
> -	/* Mutable fields. Populated with initial state. */
> -	pgoff_t pgoff;
> -	struct file *vm_file;
> -	union {
> -		vm_flags_t vm_flags;
> -		vma_flags_t vma_flags;
> -	};
> -	pgprot_t page_prot;
> -
> -	/* Write-only fields. */
> -	const struct vm_operations_struct *vm_ops;
> -	void *private_data;
> -
> -	/* Take further action? */
> -	struct mmap_action action;
> -};
> -
> -struct file_operations {
> -	int (*mmap)(struct file *, struct vm_area_struct *);
> -	int (*mmap_prepare)(struct vm_area_desc *);
> -};
> -
> -struct file {
> -	struct address_space	*f_mapping;
> -	const struct file_operations	*f_op;
> -};
> -
> -#define VMA_LOCK_OFFSET	0x40000000
> -
> -typedef struct { unsigned long v; } freeptr_t;
> -
> -struct vm_area_struct {
> -	/* The first cache line has the info for VMA tree walking. */
> -
> -	union {
> -		struct {
> -			/* VMA covers [vm_start; vm_end) addresses within mm */
> -			unsigned long vm_start;
> -			unsigned long vm_end;
> -		};
> -		freeptr_t vm_freeptr; /* Pointer used by SLAB_TYPESAFE_BY_RCU */
> -	};
> -
> -	struct mm_struct *vm_mm;	/* The address space we belong to. */
> -	pgprot_t vm_page_prot;          /* Access permissions of this VMA. */
> -
> -	/*
> -	 * Flags, see mm.h.
> -	 * To modify use vm_flags_{init|reset|set|clear|mod} functions.
> -	 */
> -	union {
> -		const vm_flags_t vm_flags;
> -		vma_flags_t flags;
> -	};
> -
> -#ifdef CONFIG_PER_VMA_LOCK
> -	/*
> -	 * Can only be written (using WRITE_ONCE()) while holding both:
> -	 *  - mmap_lock (in write mode)
> -	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set
> -	 * Can be read reliably while holding one of:
> -	 *  - mmap_lock (in read or write mode)
> -	 *  - vm_refcnt bit at VMA_LOCK_OFFSET is set or vm_refcnt > 1
> -	 * Can be read unreliably (using READ_ONCE()) for pessimistic bailout
> -	 * while holding nothing (except RCU to keep the VMA struct allocated).
> -	 *
> -	 * This sequence counter is explicitly allowed to overflow; sequence
> -	 * counter reuse can only lead to occasional unnecessary use of the
> -	 * slowpath.
> -	 */
> -	unsigned int vm_lock_seq;
> -#endif
> -
> -	/*
> -	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
> -	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
> -	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
> -	 * or brk vma (with NULL file) can only be in an anon_vma list.
> -	 */
> -	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
> -					  * page_table_lock */
> -	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */
> -
> -	/* Function pointers to deal with this struct. */
> -	const struct vm_operations_struct *vm_ops;
> -
> -	/* Information about our backing store: */
> -	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
> -					   units */
> -	struct file * vm_file;		/* File we map to (can be NULL). */
> -	void * vm_private_data;		/* was vm_pte (shared mem) */
> -
> -#ifdef CONFIG_SWAP
> -	atomic_long_t swap_readahead_info;
> -#endif
> -#ifndef CONFIG_MMU
> -	struct vm_region *vm_region;	/* NOMMU mapping region */
> -#endif
> -#ifdef CONFIG_NUMA
> -	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
> -#endif
> -#ifdef CONFIG_NUMA_BALANCING
> -	struct vma_numab_state *numab_state;	/* NUMA Balancing state */
> -#endif
> -#ifdef CONFIG_PER_VMA_LOCK
> -	/* Unstable RCU readers are allowed to read this. */
> -	refcount_t vm_refcnt;
> -#endif
> -	/*
> -	 * For areas with an address space and backing store,
> -	 * linkage into the address_space->i_mmap interval tree.
> -	 *
> -	 */
> -	struct {
> -		struct rb_node rb;
> -		unsigned long rb_subtree_last;
> -	} shared;
> -#ifdef CONFIG_ANON_VMA_NAME
> -	/*
> -	 * For private and shared anonymous mappings, a pointer to a null
> -	 * terminated string containing the name given to the vma, or NULL if
> -	 * unnamed. Serialized by mmap_lock. Use anon_vma_name to access.
> -	 */
> -	struct anon_vma_name *anon_name;
> -#endif
> -	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
> -} __randomize_layout;
> -
> -struct vm_fault {};
> -
> -struct vm_operations_struct {
> -	void (*open)(struct vm_area_struct * area);
> -	/**
> -	 * @close: Called when the VMA is being removed from the MM.
> -	 * Context: User context.  May sleep.  Caller holds mmap_lock.
> -	 */
> -	void (*close)(struct vm_area_struct * area);
> -	/* Called any time before splitting to check if it's allowed */
> -	int (*may_split)(struct vm_area_struct *area, unsigned long addr);
> -	int (*mremap)(struct vm_area_struct *area);
> -	/*
> -	 * Called by mprotect() to make driver-specific permission
> -	 * checks before mprotect() is finalised.   The VMA must not
> -	 * be modified.  Returns 0 if mprotect() can proceed.
> -	 */
> -	int (*mprotect)(struct vm_area_struct *vma, unsigned long start,
> -			unsigned long end, unsigned long newflags);
> -	vm_fault_t (*fault)(struct vm_fault *vmf);
> -	vm_fault_t (*huge_fault)(struct vm_fault *vmf, unsigned int order);
> -	vm_fault_t (*map_pages)(struct vm_fault *vmf,
> -			pgoff_t start_pgoff, pgoff_t end_pgoff);
> -	unsigned long (*pagesize)(struct vm_area_struct * area);
> -
> -	/* notification that a previously read-only page is about to become
> -	 * writable, if an error is returned it will cause a SIGBUS */
> -	vm_fault_t (*page_mkwrite)(struct vm_fault *vmf);
> -
> -	/* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */
> -	vm_fault_t (*pfn_mkwrite)(struct vm_fault *vmf);
> -
> -	/* called by access_process_vm when get_user_pages() fails, typically
> -	 * for use by special VMAs. See also generic_access_phys() for a generic
> -	 * implementation useful for any iomem mapping.
> -	 */
> -	int (*access)(struct vm_area_struct *vma, unsigned long addr,
> -		      void *buf, int len, int write);
> -
> -	/* Called by the /proc/PID/maps code to ask the vma whether it
> -	 * has a special name.  Returning non-NULL will also cause this
> -	 * vma to be dumped unconditionally. */
> -	const char *(*name)(struct vm_area_struct *vma);
> -
> -#ifdef CONFIG_NUMA
> -	/*
> -	 * set_policy() op must add a reference to any non-NULL @new mempolicy
> -	 * to hold the policy upon return.  Caller should pass NULL @new to
> -	 * remove a policy and fall back to surrounding context--i.e. do not
> -	 * install a MPOL_DEFAULT policy, nor the task or system default
> -	 * mempolicy.
> -	 */
> -	int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
> -
> -	/*
> -	 * get_policy() op must add reference [mpol_get()] to any policy at
> -	 * (vma,addr) marked as MPOL_SHARED.  The shared policy infrastructure
> -	 * in mm/mempolicy.c will do this automatically.
> -	 * get_policy() must NOT add a ref if the policy at (vma,addr) is not
> -	 * marked as MPOL_SHARED. vma policies are protected by the mmap_lock.
> -	 * If no [shared/vma] mempolicy exists at the addr, get_policy() op
> -	 * must return NULL--i.e., do not "fallback" to task or system default
> -	 * policy.
> -	 */
> -	struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
> -					unsigned long addr, pgoff_t *ilx);
> -#endif
> -#ifdef CONFIG_FIND_NORMAL_PAGE
> -	/*
> -	 * Called by vm_normal_page() for special PTEs in @vma at @addr. This
> -	 * allows for returning a "normal" page from vm_normal_page() even
> -	 * though the PTE indicates that the "struct page" either does not exist
> -	 * or should not be touched: "special".
> -	 *
> -	 * Do not add new users: this really only works when a "normal" page
> -	 * was mapped, but then the PTE got changed to something weird (+
> -	 * marked special) that would not make pte_pfn() identify the originally
> -	 * inserted page.
> -	 */
> -	struct page *(*find_normal_page)(struct vm_area_struct *vma,
> -					 unsigned long addr);
> -#endif /* CONFIG_FIND_NORMAL_PAGE */
> -};
> -
> -struct vm_unmapped_area_info {
> -#define VM_UNMAPPED_AREA_TOPDOWN 1
> -	unsigned long flags;
> -	unsigned long length;
> -	unsigned long low_limit;
> -	unsigned long high_limit;
> -	unsigned long align_mask;
> -	unsigned long align_offset;
> -	unsigned long start_gap;
> -};
> -
> -struct pagetable_move_control {
> -	struct vm_area_struct *old; /* Source VMA. */
> -	struct vm_area_struct *new; /* Destination VMA. */
> -	unsigned long old_addr; /* Address from which the move begins. */
> -	unsigned long old_end; /* Exclusive address at which old range ends. */
> -	unsigned long new_addr; /* Address to move page tables to. */
> -	unsigned long len_in; /* Bytes to remap specified by user. */
> -
> -	bool need_rmap_locks; /* Do rmap locks need to be taken? */
> -	bool for_stack; /* Is this an early temp stack being moved? */
> -};
> -
> -#define PAGETABLE_MOVE(name, old_, new_, old_addr_, new_addr_, len_)	\
> -	struct pagetable_move_control name = {				\
> -		.old = old_,						\
> -		.new = new_,						\
> -		.old_addr = old_addr_,					\
> -		.old_end = (old_addr_) + (len_),			\
> -		.new_addr = new_addr_,					\
> -		.len_in = len_,						\
> -	}
> -
> -static inline void vma_iter_invalidate(struct vma_iterator *vmi)
> -{
> -	mas_pause(&vmi->mas);
> -}
> -
> -static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
> -{
> -	return __pgprot(pgprot_val(oldprot) | pgprot_val(newprot));
> -}
> -
> -static inline pgprot_t vm_get_page_prot(vm_flags_t vm_flags)
> -{
> -	return __pgprot(vm_flags);
> -}
> -
> -static inline void vma_flags_clear_all(vma_flags_t *flags)
> -{
> -	bitmap_zero(flags->__vma_flags, NUM_VMA_FLAG_BITS);
> -}
> -
> -static inline void vma_flag_set(vma_flags_t *flags, vma_flag_t bit)
> -{
> -	unsigned long *bitmap = flags->__vma_flags;
> -
> -	__set_bit((__force int)bit, bitmap);
> -}
> -
> -static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits)
> -{
> -	vma_flags_t flags;
> -	int i;
> -
> -	vma_flags_clear_all(&flags);
> -	for (i = 0; i < count; i++)
> -		vma_flag_set(&flags, bits[i]);
> -	return flags;
> -}
> -
> -#define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \
> -					 (const vma_flag_t []){__VA_ARGS__})
> -
> -static __always_inline bool vma_flags_test_mask(const vma_flags_t *flags,
> -		vma_flags_t to_test)
> -{
> -	const unsigned long *bitmap = flags->__vma_flags;
> -	const unsigned long *bitmap_to_test = to_test.__vma_flags;
> -
> -	return bitmap_intersects(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS);
> -}
> -
> -#define vma_flags_test(flags, ...) \
> -	vma_flags_test_mask(flags, mk_vma_flags(__VA_ARGS__))
> -
> -static __always_inline bool vma_flags_test_all_mask(const vma_flags_t *flags,
> -		vma_flags_t to_test)
> -{
> -	const unsigned long *bitmap = flags->__vma_flags;
> -	const unsigned long *bitmap_to_test = to_test.__vma_flags;
> -
> -	return bitmap_subset(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS);
> -}
> -
> -#define vma_flags_test_all(flags, ...) \
> -	vma_flags_test_all_mask(flags, mk_vma_flags(__VA_ARGS__))
> -
> -static __always_inline void vma_flags_set_mask(vma_flags_t *flags, vma_flags_t to_set)
> -{
> -	unsigned long *bitmap = flags->__vma_flags;
> -	const unsigned long *bitmap_to_set = to_set.__vma_flags;
> -
> -	bitmap_or(bitmap, bitmap, bitmap_to_set, NUM_VMA_FLAG_BITS);
> -}
> -
> -#define vma_flags_set(flags, ...) \
> -	vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__))
> -
> -static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear)
> -{
> -	unsigned long *bitmap = flags->__vma_flags;
> -	const unsigned long *bitmap_to_clear = to_clear.__vma_flags;
> -
> -	bitmap_andnot(bitmap, bitmap, bitmap_to_clear, NUM_VMA_FLAG_BITS);
> -}
> -
> -#define vma_flags_clear(flags, ...) \
> -	vma_flags_clear_mask(flags, mk_vma_flags(__VA_ARGS__))
> -
> -static inline bool vma_test_all_flags_mask(const struct vm_area_struct *vma,
> -					   vma_flags_t flags)
> -{
> -	return vma_flags_test_all_mask(&vma->flags, flags);
> -}
> -
> -#define vma_test_all_flags(vma, ...) \
> -	vma_test_all_flags_mask(vma, mk_vma_flags(__VA_ARGS__))
> -
> -static inline void vma_set_flags_mask(struct vm_area_struct *vma,
> -				      vma_flags_t flags)
> -{
> -	vma_flags_set_mask(&vma->flags, flags);
> -}
> -
> -#define vma_set_flags(vma, ...) \
> -	vma_set_flags_mask(vma, mk_vma_flags(__VA_ARGS__))
> -
> -static inline bool vma_desc_test_flags_mask(const struct vm_area_desc *desc,
> -					    vma_flags_t flags)
> -{
> -	return vma_flags_test_mask(&desc->vma_flags, flags);
> -}
> -
> -#define vma_desc_test_flags(desc, ...) \
> -	vma_desc_test_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> -
> -static inline void vma_desc_set_flags_mask(struct vm_area_desc *desc,
> -					   vma_flags_t flags)
> -{
> -	vma_flags_set_mask(&desc->vma_flags, flags);
> -}
> -
> -#define vma_desc_set_flags(desc, ...) \
> -	vma_desc_set_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> -
> -static inline void vma_desc_clear_flags_mask(struct vm_area_desc *desc,
> -					     vma_flags_t flags)
> -{
> -	vma_flags_clear_mask(&desc->vma_flags, flags);
> -}
> -
> -#define vma_desc_clear_flags(desc, ...) \
> -	vma_desc_clear_flags_mask(desc, mk_vma_flags(__VA_ARGS__))
> -
> -static inline bool is_shared_maywrite_vm_flags(vm_flags_t vm_flags)
> -{
> -	return (vm_flags & (VM_SHARED | VM_MAYWRITE)) ==
> -		(VM_SHARED | VM_MAYWRITE);
> -}
> -
> -static inline bool is_shared_maywrite(const vma_flags_t *flags)
> -{
> -	return vma_flags_test_all(flags, VMA_SHARED_BIT, VMA_MAYWRITE_BIT);
> -}
> -
> -static inline bool vma_is_shared_maywrite(struct vm_area_struct *vma)
> -{
> -	return is_shared_maywrite(&vma->flags);
> -}
> -
> -static inline struct vm_area_struct *vma_next(struct vma_iterator *vmi)
> -{
> -	/*
> -	 * Uses mas_find() to get the first VMA when the iterator starts.
> -	 * Calling mas_next() could skip the first entry.
> -	 */
> -	return mas_find(&vmi->mas, ULONG_MAX);
> -}
> -
> -/*
> - * WARNING: to avoid racing with vma_mark_attached()/vma_mark_detached(), these
> - * assertions should be made either under mmap_write_lock or when the object
> - * has been isolated under mmap_write_lock, ensuring no competing writers.
> - */
> -static inline void vma_assert_attached(struct vm_area_struct *vma)
> -{
> -	WARN_ON_ONCE(!refcount_read(&vma->vm_refcnt));
> -}
> -
> -static inline void vma_assert_detached(struct vm_area_struct *vma)
> -{
> -	WARN_ON_ONCE(refcount_read(&vma->vm_refcnt));
> -}
> -
> -static inline void vma_assert_write_locked(struct vm_area_struct *);
> -static inline void vma_mark_attached(struct vm_area_struct *vma)
> -{
> -	vma_assert_write_locked(vma);
> -	vma_assert_detached(vma);
> -	refcount_set_release(&vma->vm_refcnt, 1);
> -}
> -
> -static inline void vma_mark_detached(struct vm_area_struct *vma)
> -{
> -	vma_assert_write_locked(vma);
> -	vma_assert_attached(vma);
> -	/* We are the only writer, so no need to use vma_refcount_put(). */
> -	if (unlikely(!refcount_dec_and_test(&vma->vm_refcnt))) {
> -		/*
> -		 * Reader must have temporarily raised vm_refcnt but it will
> -		 * drop it without using the vma since vma is write-locked.
> -		 */
> -	}
> -}
> -
> -extern const struct vm_operations_struct vma_dummy_vm_ops;
> -
> -extern unsigned long rlimit(unsigned int limit);
> -
> -static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
> -{
> -	memset(vma, 0, sizeof(*vma));
> -	vma->vm_mm = mm;
> -	vma->vm_ops = &vma_dummy_vm_ops;
> -	INIT_LIST_HEAD(&vma->anon_vma_chain);
> -	vma->vm_lock_seq = UINT_MAX;
> -}
> -
> -/*
> - * These are defined in vma.h, but sadly vm_stat_account() is referenced by
> - * kernel/fork.c, so we have to these broadly available there, and temporarily
> - * define them here to resolve the dependency cycle.
> - */
> -
> -#define is_exec_mapping(flags) \
> -	((flags & (VM_EXEC | VM_WRITE | VM_STACK)) == VM_EXEC)
> -
> -#define is_stack_mapping(flags) \
> -	(((flags & VM_STACK) == VM_STACK) || (flags & VM_SHADOW_STACK))
> -
> -#define is_data_mapping(flags) \
> -	((flags & (VM_WRITE | VM_SHARED | VM_STACK)) == VM_WRITE)
> -
> -static inline void vm_stat_account(struct mm_struct *mm, vm_flags_t flags,
> -				   long npages)
> -{
> -	WRITE_ONCE(mm->total_vm, READ_ONCE(mm->total_vm)+npages);
> -
> -	if (is_exec_mapping(flags))
> -		mm->exec_vm += npages;
> -	else if (is_stack_mapping(flags))
> -		mm->stack_vm += npages;
> -	else if (is_data_mapping(flags))
> -		mm->data_vm += npages;
> -}
> -
> -#undef is_exec_mapping
> -#undef is_stack_mapping
> -#undef is_data_mapping
> -
> -/* Currently stubbed but we may later wish to un-stub. */
> -static inline void vm_acct_memory(long pages);
> -static inline void vm_unacct_memory(long pages)
> -{
> -	vm_acct_memory(-pages);
> -}
> -
> -static inline void mapping_allow_writable(struct address_space *mapping)
> -{
> -	atomic_inc(&mapping->i_mmap_writable);
> -}
> -
> -static inline
> -struct vm_area_struct *vma_find(struct vma_iterator *vmi, unsigned long max)
> -{
> -	return mas_find(&vmi->mas, max - 1);
> -}
> -
> -static inline int vma_iter_clear_gfp(struct vma_iterator *vmi,
> -			unsigned long start, unsigned long end, gfp_t gfp)
> -{
> -	__mas_set_range(&vmi->mas, start, end - 1);
> -	mas_store_gfp(&vmi->mas, NULL, gfp);
> -	if (unlikely(mas_is_err(&vmi->mas)))
> -		return -ENOMEM;
> -
> -	return 0;
> -}
> -
> -static inline void mmap_assert_locked(struct mm_struct *);
> -static inline struct vm_area_struct *find_vma_intersection(struct mm_struct *mm,
> -						unsigned long start_addr,
> -						unsigned long end_addr)
> -{
> -	unsigned long index = start_addr;
> -
> -	mmap_assert_locked(mm);
> -	return mt_find(&mm->mm_mt, &index, end_addr - 1);
> -}
> -
> -static inline
> -struct vm_area_struct *vma_lookup(struct mm_struct *mm, unsigned long addr)
> -{
> -	return mtree_load(&mm->mm_mt, addr);
> -}
> -
> -static inline struct vm_area_struct *vma_prev(struct vma_iterator *vmi)
> -{
> -	return mas_prev(&vmi->mas, 0);
> -}
> -
> -static inline void vma_iter_set(struct vma_iterator *vmi, unsigned long addr)
> -{
> -	mas_set(&vmi->mas, addr);
> -}
> -
> -static inline bool vma_is_anonymous(struct vm_area_struct *vma)
> -{
> -	return !vma->vm_ops;
> -}
> -
> -/* Defined in vma.h, so temporarily define here to avoid circular dependency. */
> -#define vma_iter_load(vmi) \
> -	mas_walk(&(vmi)->mas)
> -
> -static inline struct vm_area_struct *
> -find_vma_prev(struct mm_struct *mm, unsigned long addr,
> -			struct vm_area_struct **pprev)
> -{
> -	struct vm_area_struct *vma;
> -	VMA_ITERATOR(vmi, mm, addr);
> -
> -	vma = vma_iter_load(&vmi);
> -	*pprev = vma_prev(&vmi);
> -	if (!vma)
> -		vma = vma_next(&vmi);
> -	return vma;
> -}
> -
> -#undef vma_iter_load
> -
> -static inline void vma_iter_init(struct vma_iterator *vmi,
> -		struct mm_struct *mm, unsigned long addr)
> -{
> -	mas_init(&vmi->mas, &mm->mm_mt, addr);
> -}
> -
> -/* Stubbed functions. */
> -
> -static inline struct anon_vma_name *anon_vma_name(struct vm_area_struct *vma)
> -{
> -	return NULL;
> -}
> -
> -static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma,
> -					struct vm_userfaultfd_ctx vm_ctx)
> -{
> -	return true;
> -}
> -
> -static inline bool anon_vma_name_eq(struct anon_vma_name *anon_name1,
> -				    struct anon_vma_name *anon_name2)
> -{
> -	return true;
> -}
> -
> -static inline void might_sleep(void)
> -{
> -}
> -
> -static inline unsigned long vma_pages(struct vm_area_struct *vma)
> -{
> -	return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> -}
> -
> -static inline void fput(struct file *file)
> -{
> -}
> -
> -static inline void mpol_put(struct mempolicy *pol)
> -{
> -}
> -
> -static inline void lru_add_drain(void)
> -{
> -}
> -
> -static inline void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm)
> -{
> -}
> -
> -static inline void update_hiwater_rss(struct mm_struct *mm)
> -{
> -}
> -
> -static inline void update_hiwater_vm(struct mm_struct *mm)
> -{
> -}
> -
> -struct unmap_desc;
> -
> -static inline void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap)
> -{
> -}
> -
> -static inline void free_pgtables(struct mmu_gather *tlb, struct unmap_desc *desc)
> -{
> -	(void)tlb;
> -	(void)desc;
> -}
> -
> -static inline void mapping_unmap_writable(struct address_space *mapping)
> -{
> -}
> -
> -static inline void flush_dcache_mmap_lock(struct address_space *mapping)
> -{
> -}
> -
> -static inline void tlb_finish_mmu(struct mmu_gather *tlb)
> -{
> -}
> -
> -static inline struct file *get_file(struct file *f)
> -{
> -	return f;
> -}
> -
> -static inline int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst)
> -{
> -	return 0;
> -}
> -
> -static inline int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src,
> -				 enum vma_operation operation)
> -{
> -	/* For testing purposes. We indicate that an anon_vma has been cloned. */
> -	if (src->anon_vma != NULL) {
> -		dst->anon_vma = src->anon_vma;
> -		dst->anon_vma->was_cloned = true;
> -	}
> -
> -	return 0;
> -}
> -
> -static inline void vma_start_write(struct vm_area_struct *vma)
> -{
> -	/* Used to indicate to tests that a write operation has begun. */
> -	vma->vm_lock_seq++;
> -}
> -
> -static inline __must_check
> -int vma_start_write_killable(struct vm_area_struct *vma)
> -{
> -	/* Used to indicate to tests that a write operation has begun. */
> -	vma->vm_lock_seq++;
> -	return 0;
> -}
> -
> -static inline void vma_adjust_trans_huge(struct vm_area_struct *vma,
> -					 unsigned long start,
> -					 unsigned long end,
> -					 struct vm_area_struct *next)
> -{
> -}
> -
> -static inline void hugetlb_split(struct vm_area_struct *, unsigned long) {}
> -
> -static inline void vma_iter_free(struct vma_iterator *vmi)
> -{
> -	mas_destroy(&vmi->mas);
> -}
> -
> -static inline
> -struct vm_area_struct *vma_iter_next_range(struct vma_iterator *vmi)
> -{
> -	return mas_next_range(&vmi->mas, ULONG_MAX);
> -}
> -
> -static inline void vm_acct_memory(long pages)
> -{
> -}
> -
> -static inline void vma_interval_tree_insert(struct vm_area_struct *vma,
> -					    struct rb_root_cached *rb)
> -{
> -}
> -
> -static inline void vma_interval_tree_remove(struct vm_area_struct *vma,
> -					    struct rb_root_cached *rb)
> -{
> -}
> -
> -static inline void flush_dcache_mmap_unlock(struct address_space *mapping)
> -{
> -}
> -
> -static inline void anon_vma_interval_tree_insert(struct anon_vma_chain *avc,
> -						 struct rb_root_cached *rb)
> -{
> -}
> -
> -static inline void anon_vma_interval_tree_remove(struct anon_vma_chain *avc,
> -						 struct rb_root_cached *rb)
> -{
> -}
> -
> -static inline void uprobe_mmap(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void uprobe_munmap(struct vm_area_struct *vma,
> -				 unsigned long start, unsigned long end)
> -{
> -}
> -
> -static inline void i_mmap_lock_write(struct address_space *mapping)
> -{
> -}
> -
> -static inline void anon_vma_lock_write(struct anon_vma *anon_vma)
> -{
> -}
> -
> -static inline void vma_assert_write_locked(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void unlink_anon_vmas(struct vm_area_struct *vma)
> -{
> -	/* For testing purposes, indicate that the anon_vma was unlinked. */
> -	vma->anon_vma->was_unlinked = true;
> -}
> -
> -static inline void anon_vma_unlock_write(struct anon_vma *anon_vma)
> -{
> -}
> -
> -static inline void i_mmap_unlock_write(struct address_space *mapping)
> -{
> -}
> -
> -static inline int userfaultfd_unmap_prep(struct vm_area_struct *vma,
> -					 unsigned long start,
> -					 unsigned long end,
> -					 struct list_head *unmaps)
> -{
> -	return 0;
> -}
> -
> -static inline void mmap_write_downgrade(struct mm_struct *mm)
> -{
> -}
> -
> -static inline void mmap_read_unlock(struct mm_struct *mm)
> -{
> -}
> -
> -static inline void mmap_write_unlock(struct mm_struct *mm)
> -{
> -}
> -
> -static inline int mmap_write_lock_killable(struct mm_struct *mm)
> -{
> -	return 0;
> -}
> -
> -static inline bool can_modify_mm(struct mm_struct *mm,
> -				 unsigned long start,
> -				 unsigned long end)
> -{
> -	return true;
> -}
> -
> -static inline void arch_unmap(struct mm_struct *mm,
> -				 unsigned long start,
> -				 unsigned long end)
> -{
> -}
> -
> -static inline void mmap_assert_locked(struct mm_struct *mm)
> -{
> -}
> -
> -static inline bool mpol_equal(struct mempolicy *a, struct mempolicy *b)
> -{
> -	return true;
> -}
> -
> -static inline void khugepaged_enter_vma(struct vm_area_struct *vma,
> -			  vm_flags_t vm_flags)
> -{
> -}
> -
> -static inline bool mapping_can_writeback(struct address_space *mapping)
> -{
> -	return true;
> -}
> -
> -static inline bool is_vm_hugetlb_page(struct vm_area_struct *vma)
> -{
> -	return false;
> -}
> -
> -static inline bool vma_soft_dirty_enabled(struct vm_area_struct *vma)
> -{
> -	return false;
> -}
> -
> -static inline bool userfaultfd_wp(struct vm_area_struct *vma)
> -{
> -	return false;
> -}
> -
> -static inline void mmap_assert_write_locked(struct mm_struct *mm)
> -{
> -}
> -
> -static inline void mutex_lock(struct mutex *lock)
> -{
> -}
> -
> -static inline void mutex_unlock(struct mutex *lock)
> -{
> -}
> -
> -static inline bool mutex_is_locked(struct mutex *lock)
> -{
> -	return true;
> -}
> -
> -static inline bool signal_pending(void *p)
> -{
> -	return false;
> -}
> -
> -static inline bool is_file_hugepages(struct file *file)
> -{
> -	return false;
> -}
> -
> -static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
> -{
> -	return 0;
> -}
> -
> -static inline bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags,
> -				 unsigned long npages)
> -{
> -	return true;
> -}
> -
> -static inline int shmem_zero_setup(struct vm_area_struct *vma)
> -{
> -	return 0;
> -}
> -
> -static inline void vma_set_anonymous(struct vm_area_struct *vma)
> -{
> -	vma->vm_ops = NULL;
> -}
> -
> -static inline void ksm_add_vma(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void perf_event_mmap(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline bool vma_is_dax(struct vm_area_struct *vma)
> -{
> -	return false;
> -}
> -
> -static inline struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
> -{
> -	return NULL;
> -}
> -
> -bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot);
> -
> -/* Update vma->vm_page_prot to reflect vma->vm_flags. */
> -static inline void vma_set_page_prot(struct vm_area_struct *vma)
> -{
> -	vm_flags_t vm_flags = vma->vm_flags;
> -	pgprot_t vm_page_prot;
> -
> -	/* testing: we inline vm_pgprot_modify() to avoid clash with vma.h. */
> -	vm_page_prot = pgprot_modify(vma->vm_page_prot, vm_get_page_prot(vm_flags));
> -
> -	if (vma_wants_writenotify(vma, vm_page_prot)) {
> -		vm_flags &= ~VM_SHARED;
> -		/* testing: we inline vm_pgprot_modify() to avoid clash with vma.h. */
> -		vm_page_prot = pgprot_modify(vm_page_prot, vm_get_page_prot(vm_flags));
> -	}
> -	/* remove_protection_ptes reads vma->vm_page_prot without mmap_lock */
> -	WRITE_ONCE(vma->vm_page_prot, vm_page_prot);
> -}
> -
> -static inline bool arch_validate_flags(vm_flags_t flags)
> -{
> -	return true;
> -}
> -
> -static inline void vma_close(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline int mmap_file(struct file *file, struct vm_area_struct *vma)
> -{
> -	return 0;
> -}
> -
> -static inline unsigned long stack_guard_start_gap(struct vm_area_struct *vma)
> -{
> -	if (vma->vm_flags & VM_GROWSDOWN)
> -		return stack_guard_gap;
> -
> -	/* See reasoning around the VM_SHADOW_STACK definition */
> -	if (vma->vm_flags & VM_SHADOW_STACK)
> -		return PAGE_SIZE;
> -
> -	return 0;
> -}
> -
> -static inline unsigned long vm_start_gap(struct vm_area_struct *vma)
> -{
> -	unsigned long gap = stack_guard_start_gap(vma);
> -	unsigned long vm_start = vma->vm_start;
> -
> -	vm_start -= gap;
> -	if (vm_start > vma->vm_start)
> -		vm_start = 0;
> -	return vm_start;
> -}
> -
> -static inline unsigned long vm_end_gap(struct vm_area_struct *vma)
> -{
> -	unsigned long vm_end = vma->vm_end;
> -
> -	if (vma->vm_flags & VM_GROWSUP) {
> -		vm_end += stack_guard_gap;
> -		if (vm_end < vma->vm_end)
> -			vm_end = -PAGE_SIZE;
> -	}
> -	return vm_end;
> -}
> -
> -static inline int is_hugepage_only_range(struct mm_struct *mm,
> -					unsigned long addr, unsigned long len)
> -{
> -	return 0;
> -}
> -
> -static inline bool vma_is_accessible(struct vm_area_struct *vma)
> -{
> -	return vma->vm_flags & VM_ACCESS_FLAGS;
> -}
> -
> -static inline bool capable(int cap)
> -{
> -	return true;
> -}
> -
> -static inline bool mlock_future_ok(const struct mm_struct *mm,
> -		vm_flags_t vm_flags, unsigned long bytes)
> -{
> -	unsigned long locked_pages, limit_pages;
> -
> -	if (!(vm_flags & VM_LOCKED) || capable(CAP_IPC_LOCK))
> -		return true;
> -
> -	locked_pages = bytes >> PAGE_SHIFT;
> -	locked_pages += mm->locked_vm;
> -
> -	limit_pages = rlimit(RLIMIT_MEMLOCK);
> -	limit_pages >>= PAGE_SHIFT;
> -
> -	return locked_pages <= limit_pages;
> -}
> -
> -static inline int __anon_vma_prepare(struct vm_area_struct *vma)
> -{
> -	struct anon_vma *anon_vma = calloc(1, sizeof(struct anon_vma));
> -
> -	if (!anon_vma)
> -		return -ENOMEM;
> -
> -	anon_vma->root = anon_vma;
> -	vma->anon_vma = anon_vma;
> -
> -	return 0;
> -}
> -
> -static inline int anon_vma_prepare(struct vm_area_struct *vma)
> -{
> -	if (likely(vma->anon_vma))
> -		return 0;
> -
> -	return __anon_vma_prepare(vma);
> -}
> -
> -static inline void userfaultfd_unmap_complete(struct mm_struct *mm,
> -					      struct list_head *uf)
> -{
> -}
> -
> -static inline bool mm_flags_test(int flag, const struct mm_struct *mm)
> -{
> -	return test_bit(flag, ACCESS_PRIVATE(&mm->flags, __mm_flags));
> -}
> -
> -/*
> - * Copy value to the first system word of VMA flags, non-atomically.
> - *
> - * IMPORTANT: This does not overwrite bytes past the first system word. The
> - * caller must account for this.
> - */
> -static inline void vma_flags_overwrite_word(vma_flags_t *flags, unsigned long value)
> -{
> -	*ACCESS_PRIVATE(flags, __vma_flags) = value;
> -}
> -
> -/*
> - * Copy value to the first system word of VMA flags ONCE, non-atomically.
> - *
> - * IMPORTANT: This does not overwrite bytes past the first system word. The
> - * caller must account for this.
> - */
> -static inline void vma_flags_overwrite_word_once(vma_flags_t *flags, unsigned long value)
> -{
> -	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> -
> -	WRITE_ONCE(*bitmap, value);
> -}
> -
> -/* Update the first system word of VMA flags setting bits, non-atomically. */
> -static inline void vma_flags_set_word(vma_flags_t *flags, unsigned long value)
> -{
> -	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> -
> -	*bitmap |= value;
> -}
> -
> -/* Update the first system word of VMA flags clearing bits, non-atomically. */
> -static inline void vma_flags_clear_word(vma_flags_t *flags, unsigned long value)
> -{
> -	unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags);
> -
> -	*bitmap &= ~value;
> -}
> -
> -
> -/* Use when VMA is not part of the VMA tree and needs no locking */
> -static inline void vm_flags_init(struct vm_area_struct *vma,
> -				 vm_flags_t flags)
> -{
> -	vma_flags_clear_all(&vma->flags);
> -	vma_flags_overwrite_word(&vma->flags, flags);
> -}
> -
> -/*
> - * Use when VMA is part of the VMA tree and modifications need coordination
> - * Note: vm_flags_reset and vm_flags_reset_once do not lock the vma and
> - * it should be locked explicitly beforehand.
> - */
> -static inline void vm_flags_reset(struct vm_area_struct *vma,
> -				  vm_flags_t flags)
> -{
> -	vma_assert_write_locked(vma);
> -	vm_flags_init(vma, flags);
> -}
> -
> -static inline void vm_flags_reset_once(struct vm_area_struct *vma,
> -				       vm_flags_t flags)
> -{
> -	vma_assert_write_locked(vma);
> -	/*
> -	 * The user should only be interested in avoiding reordering of
> -	 * assignment to the first word.
> -	 */
> -	vma_flags_clear_all(&vma->flags);
> -	vma_flags_overwrite_word_once(&vma->flags, flags);
> -}
> -
> -static inline void vm_flags_set(struct vm_area_struct *vma,
> -				vm_flags_t flags)
> -{
> -	vma_start_write(vma);
> -	vma_flags_set_word(&vma->flags, flags);
> -}
> -
> -static inline void vm_flags_clear(struct vm_area_struct *vma,
> -				  vm_flags_t flags)
> -{
> -	vma_start_write(vma);
> -	vma_flags_clear_word(&vma->flags, flags);
> -}
> -
> -/*
> - * Denies creating a writable executable mapping or gaining executable permissions.
> - *
> - * This denies the following:
> - *
> - *     a)      mmap(PROT_WRITE | PROT_EXEC)
> - *
> - *     b)      mmap(PROT_WRITE)
> - *             mprotect(PROT_EXEC)
> - *
> - *     c)      mmap(PROT_WRITE)
> - *             mprotect(PROT_READ)
> - *             mprotect(PROT_EXEC)
> - *
> - * But allows the following:
> - *
> - *     d)      mmap(PROT_READ | PROT_EXEC)
> - *             mmap(PROT_READ | PROT_EXEC | PROT_BTI)
> - *
> - * This is only applicable if the user has set the Memory-Deny-Write-Execute
> - * (MDWE) protection mask for the current process.
> - *
> - * @old specifies the VMA flags the VMA originally possessed, and @new the ones
> - * we propose to set.
> - *
> - * Return: false if proposed change is OK, true if not ok and should be denied.
> - */
> -static inline bool map_deny_write_exec(unsigned long old, unsigned long new)
> -{
> -	/* If MDWE is disabled, we have nothing to deny. */
> -	if (mm_flags_test(MMF_HAS_MDWE, current->mm))
> -		return false;
> -
> -	/* If the new VMA is not executable, we have nothing to deny. */
> -	if (!(new & VM_EXEC))
> -		return false;
> -
> -	/* Under MDWE we do not accept newly writably executable VMAs... */
> -	if (new & VM_WRITE)
> -		return true;
> -
> -	/* ...nor previously non-executable VMAs becoming executable. */
> -	if (!(old & VM_EXEC))
> -		return true;
> -
> -	return false;
> -}
> -
> -static inline int mapping_map_writable(struct address_space *mapping)
> -{
> -	return atomic_inc_unless_negative(&mapping->i_mmap_writable) ?
> -		0 : -EPERM;
> -}
> -
> -static inline unsigned long move_page_tables(struct pagetable_move_control *pmc)
> -{
> -	return 0;
> -}
> -
> -static inline void free_pgd_range(struct mmu_gather *tlb,
> -			unsigned long addr, unsigned long end,
> -			unsigned long floor, unsigned long ceiling)
> -{
> -}
> -
> -static inline int ksm_execve(struct mm_struct *mm)
> -{
> -	return 0;
> -}
> -
> -static inline void ksm_exit(struct mm_struct *mm)
> -{
> -}
> -
> -static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt)
> -{
> -	if (reset_refcnt)
> -		refcount_set(&vma->vm_refcnt, 0);
> -}
> -
> -static inline void vma_numab_state_init(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void vma_numab_state_free(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void dup_anon_vma_name(struct vm_area_struct *orig_vma,
> -				     struct vm_area_struct *new_vma)
> -{
> -}
> -
> -static inline void free_anon_vma_name(struct vm_area_struct *vma)
> -{
> -}
> -
> -/* Declared in vma.h. */
> -static inline void set_vma_from_desc(struct vm_area_struct *vma,
> -		struct vm_area_desc *desc);
> -
> -static inline void mmap_action_prepare(struct mmap_action *action,
> -					   struct vm_area_desc *desc)
> -{
> -}
> -
> -static inline int mmap_action_complete(struct mmap_action *action,
> -					   struct vm_area_struct *vma)
> -{
> -	return 0;
> -}
> -
> -static inline int __compat_vma_mmap(const struct file_operations *f_op,
> -		struct file *file, struct vm_area_struct *vma)
> -{
> -	struct vm_area_desc desc = {
> -		.mm = vma->vm_mm,
> -		.file = file,
> -		.start = vma->vm_start,
> -		.end = vma->vm_end,
> -
> -		.pgoff = vma->vm_pgoff,
> -		.vm_file = vma->vm_file,
> -		.vm_flags = vma->vm_flags,
> -		.page_prot = vma->vm_page_prot,
> -
> -		.action.type = MMAP_NOTHING, /* Default */
> -	};
> -	int err;
> -
> -	err = f_op->mmap_prepare(&desc);
> -	if (err)
> -		return err;
> -
> -	mmap_action_prepare(&desc.action, &desc);
> -	set_vma_from_desc(vma, &desc);
> -	return mmap_action_complete(&desc.action, vma);
> -}
> -
> -static inline int compat_vma_mmap(struct file *file,
> -		struct vm_area_struct *vma)
> -{
> -	return __compat_vma_mmap(file->f_op, file, vma);
> -}
> -
> -/* Did the driver provide valid mmap hook configuration? */
> -static inline bool can_mmap_file(struct file *file)
> -{
> -	bool has_mmap = file->f_op->mmap;
> -	bool has_mmap_prepare = file->f_op->mmap_prepare;
> -
> -	/* Hooks are mutually exclusive. */
> -	if (WARN_ON_ONCE(has_mmap && has_mmap_prepare))
> -		return false;
> -	if (!has_mmap && !has_mmap_prepare)
> -		return false;
> -
> -	return true;
> -}
> -
> -static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)
> -{
> -	if (file->f_op->mmap_prepare)
> -		return compat_vma_mmap(file, vma);
> -
> -	return file->f_op->mmap(file, vma);
> -}
> -
> -static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc)
> -{
> -	return file->f_op->mmap_prepare(desc);
> -}
> -
> -static inline void fixup_hugetlb_reservations(struct vm_area_struct *vma)
> -{
> -}
> -
> -static inline void vma_set_file(struct vm_area_struct *vma, struct file *file)
> -{
> -	/* Changing an anonymous vma with this is illegal */
> -	get_file(file);
> -	swap(vma->vm_file, file);
> -	fput(file);
> -}
> -
> -static inline bool shmem_file(struct file *file)
> -{
> -	return false;
> -}
> -
> -static inline vm_flags_t ksm_vma_flags(const struct mm_struct *mm,
> -		const struct file *file, vm_flags_t vm_flags)
> -{
> -	return vm_flags;
> -}
> -
> -static inline void remap_pfn_range_prepare(struct vm_area_desc *desc, unsigned long pfn)
> -{
> -}
> -
> -static inline int remap_pfn_range_complete(struct vm_area_struct *vma, unsigned long addr,
> -		unsigned long pfn, unsigned long size, pgprot_t pgprot)
> -{
> -	return 0;
> -}
> +typedef unsigned long vm_flags_t;
> +#define pgoff_t unsigned long
> +typedef unsigned long	pgprotval_t;
> +typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;
> +typedef __bitwise unsigned int vm_fault_t;
>  
> -static inline int do_munmap(struct mm_struct *, unsigned long, size_t,
> -		struct list_head *uf)
> -{
> -	return 0;
> -}
> +#include "include/stubs.h"
> +#include "include/dup.h"
> +#include "include/custom.h"
>  
>  #endif	/* __MM_VMA_INTERNAL_H */
> -- 
> 2.52.0
> 

^ permalink raw reply

* Re: [PATCH v2 13/13] tools/testing/vma: add VMA userland tests for VMA flag functions
From: Liam R. Howlett @ 2026-02-09 20:23 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Andrew Morton, Jarkko Sakkinen, Dave Hansen, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, x86, H . Peter Anvin, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
	Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
	Matthew Brost, Alexander Viro, Christian Brauner, Jan Kara,
	Benjamin LaHaise, Gao Xiang, Chao Yu, Yue Hu, Jeffle Xu,
	Sandeep Dhavale, Hongbo Li, Chunhai Guo, Theodore Ts'o,
	Andreas Dilger, Muchun Song, Oscar Salvador, David Hildenbrand,
	Konstantin Komarov, Mike Marshall, Martin Brandenburg, Tony Luck,
	Reinette Chatre, Dave Martin, James Morse, Babu Moger,
	Carlos Maiolino, Damien Le Moal, Naohiro Aota, Johannes Thumshirn,
	Matthew Wilcox, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Hugh Dickins, Baolin Wang,
	Zi Yan, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
	Lance Yang, Jann Horn, Pedro Falcato, David Howells, Paul Moore,
	James Morris, Serge E . Hallyn, Yury Norov, Rasmus Villemoes,
	linux-sgx, linux-kernel, nvdimm, linux-cxl, dri-devel, intel-gfx,
	linux-fsdevel, linux-aio, linux-erofs, linux-ext4, linux-mm,
	ntfs3, devel, linux-xfs, keyrings, linux-security-module,
	Jason Gunthorpe
In-Reply-To: <7fe6afe9c8c61e4d3cfc9a2d50a5d24da8528e68.1769097829.git.lorenzo.stoakes@oracle.com>

* Lorenzo Stoakes <lorenzo.stoakes@oracle.com> [260122 16:06]:
> Now we have the capability to test the new helpers for the bitmap VMA flags
> in userland, do so.
> 
> We also update the Makefile such that both VMA (and while we're here)
> mm_struct flag sizes can be customised on build. We default to 128-bit to
> enable testing of flags above word size even on 64-bit systems.
> 
> We add userland tests to ensure that we do not regress VMA flag behaviour
> with the introduction when using bitmap VMA flags, nor accidentally
> introduce unexpected results due to for instance higher bit values not
> being correctly cleared/set.
> 
> As part of this change, make __mk_vma_flags() a custom function so we can
> handle specifying invalid VMA bits. This is purposeful so we can have the
> VMA tests work at lower and higher number of VMA flags without having to
> duplicate code too much.
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>

Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>

> ---
>  tools/testing/vma/Makefile         |   3 +
>  tools/testing/vma/include/custom.h |  16 ++
>  tools/testing/vma/include/dup.h    |  11 +-
>  tools/testing/vma/tests/vma.c      | 300 +++++++++++++++++++++++++++++
>  tools/testing/vma/vma_internal.h   |   4 +-
>  5 files changed, 322 insertions(+), 12 deletions(-)
> 
> diff --git a/tools/testing/vma/Makefile b/tools/testing/vma/Makefile
> index 50aa4301b3a6..e72b45dedda5 100644
> --- a/tools/testing/vma/Makefile
> +++ b/tools/testing/vma/Makefile
> @@ -9,6 +9,9 @@ include ../shared/shared.mk
>  OFILES = $(SHARED_OFILES) main.o shared.o maple-shim.o
>  TARGETS = vma
>  
> +# These can be varied to test different sizes.
> +CFLAGS += -DNUM_VMA_FLAG_BITS=128 -DNUM_MM_FLAG_BITS=128
> +
>  main.o: main.c shared.c shared.h vma_internal.h tests/merge.c tests/mmap.c tests/vma.c ../../../mm/vma.c ../../../mm/vma_init.c ../../../mm/vma_exec.c ../../../mm/vma.h include/custom.h include/dup.h include/stubs.h
>  
>  vma:	$(OFILES)
> diff --git a/tools/testing/vma/include/custom.h b/tools/testing/vma/include/custom.h
> index f567127efba9..802a76317245 100644
> --- a/tools/testing/vma/include/custom.h
> +++ b/tools/testing/vma/include/custom.h
> @@ -101,3 +101,19 @@ static inline void vma_lock_init(struct vm_area_struct *vma, bool reset_refcnt)
>  	if (reset_refcnt)
>  		refcount_set(&vma->vm_refcnt, 0);
>  }
> +
> +static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits)
> +{
> +	vma_flags_t flags;
> +	int i;
> +
> +	/*
> +	 * For testing purposes: allow invalid bit specification so we can
> +	 * easily test.
> +	 */
> +	vma_flags_clear_all(&flags);
> +	for (i = 0; i < count; i++)
> +		if (bits[i] < NUM_VMA_FLAG_BITS)
> +			vma_flag_set(&flags, bits[i]);
> +	return flags;
> +}
> diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
> index ed8708afb7af..31ee02f709b2 100644
> --- a/tools/testing/vma/include/dup.h
> +++ b/tools/testing/vma/include/dup.h
> @@ -838,16 +838,7 @@ static inline void vm_flags_clear(struct vm_area_struct *vma,
>  	vma_flags_clear_word(&vma->flags, flags);
>  }
>  
> -static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits)
> -{
> -	vma_flags_t flags;
> -	int i;
> -
> -	vma_flags_clear_all(&flags);
> -	for (i = 0; i < count; i++)
> -		vma_flag_set(&flags, bits[i]);
> -	return flags;
> -}
> +static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits);
>  
>  #define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \
>  					 (const vma_flag_t []){__VA_ARGS__})
> diff --git a/tools/testing/vma/tests/vma.c b/tools/testing/vma/tests/vma.c
> index 6d9775aee243..c54ffc954f11 100644
> --- a/tools/testing/vma/tests/vma.c
> +++ b/tools/testing/vma/tests/vma.c
> @@ -1,5 +1,25 @@
>  // SPDX-License-Identifier: GPL-2.0-or-later
>  
> +static bool compare_legacy_flags(vm_flags_t legacy_flags, vma_flags_t flags)
> +{
> +	const unsigned long legacy_val = legacy_flags;
> +	/* The lower word should contain the precise same value. */
> +	const unsigned long flags_lower = flags.__vma_flags[0];
> +#if NUM_VMA_FLAGS > BITS_PER_LONG
> +	int i;
> +
> +	/* All bits in higher flag values should be zero. */
> +	for (i = 1; i < NUM_VMA_FLAGS / BITS_PER_LONG; i++) {
> +		if (flags.__vma_flags[i] != 0)
> +			return false;
> +	}
> +#endif
> +
> +	static_assert(sizeof(legacy_flags) == sizeof(unsigned long));
> +
> +	return legacy_val == flags_lower;
> +}
> +
>  static bool test_copy_vma(void)
>  {
>  	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> @@ -33,7 +53,287 @@ static bool test_copy_vma(void)
>  	return true;
>  }
>  
> +static bool test_vma_flags_unchanged(void)
> +{
> +	vma_flags_t flags = EMPTY_VMA_FLAGS;
> +	vm_flags_t legacy_flags = 0;
> +	int bit;
> +	struct vm_area_struct vma;
> +	struct vm_area_desc desc;
> +
> +
> +	vma.flags = EMPTY_VMA_FLAGS;
> +	desc.vma_flags = EMPTY_VMA_FLAGS;
> +
> +	for (bit = 0; bit < BITS_PER_LONG; bit++) {
> +		vma_flags_t mask = mk_vma_flags(bit);
> +
> +		legacy_flags |= (1UL << bit);
> +
> +		/* Individual flags. */
> +		vma_flags_set(&flags, bit);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, flags));
> +
> +		/* Via mask. */
> +		vma_flags_set_mask(&flags, mask);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, flags));
> +
> +		/* Same for VMA. */
> +		vma_set_flags(&vma, bit);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, vma.flags));
> +		vma_set_flags_mask(&vma, mask);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, vma.flags));
> +
> +		/* Same for VMA descriptor. */
> +		vma_desc_set_flags(&desc, bit);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, desc.vma_flags));
> +		vma_desc_set_flags_mask(&desc, mask);
> +		ASSERT_TRUE(compare_legacy_flags(legacy_flags, desc.vma_flags));
> +	}
> +
> +	return true;
> +}
> +
> +static bool test_vma_flags_cleared(void)
> +{
> +	const vma_flags_t empty = EMPTY_VMA_FLAGS;
> +	vma_flags_t flags;
> +	int i;
> +
> +	/* Set all bits high. */
> +	memset(&flags, 1, sizeof(flags));
> +	/* Try to clear. */
> +	vma_flags_clear_all(&flags);
> +	/* Equal to EMPTY_VMA_FLAGS? */
> +	ASSERT_EQ(memcmp(&empty, &flags, sizeof(flags)), 0);
> +	/* Make sure every unsigned long entry in bitmap array zero. */
> +	for (i = 0; i < sizeof(flags) / BITS_PER_LONG; i++) {
> +		const unsigned long val = flags.__vma_flags[i];
> +
> +		ASSERT_EQ(val, 0);
> +	}
> +
> +	return true;
> +}
> +
> +/*
> + * Assert that VMA flag functions that operate at the system word level function
> + * correctly.
> + */
> +static bool test_vma_flags_word(void)
> +{
> +	vma_flags_t flags = EMPTY_VMA_FLAGS;
> +	const vma_flags_t comparison =
> +		mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, 64, 65);
> +
> +	/* Set some custom high flags. */
> +	vma_flags_set(&flags, 64, 65);
> +	/* Now overwrite the first word. */
> +	vma_flags_overwrite_word(&flags, VM_READ | VM_WRITE);
> +	/* Ensure they are equal. */
> +	ASSERT_EQ(memcmp(&flags, &comparison, sizeof(flags)), 0);
> +
> +	flags = EMPTY_VMA_FLAGS;
> +	vma_flags_set(&flags, 64, 65);
> +
> +	/* Do the same with the _once() equivalent. */
> +	vma_flags_overwrite_word_once(&flags, VM_READ | VM_WRITE);
> +	ASSERT_EQ(memcmp(&flags, &comparison, sizeof(flags)), 0);
> +
> +	flags = EMPTY_VMA_FLAGS;
> +	vma_flags_set(&flags, 64, 65);
> +
> +	/* Make sure we can set a word without disturbing other bits. */
> +	vma_flags_set(&flags, VMA_WRITE_BIT);
> +	vma_flags_set_word(&flags, VM_READ);
> +	ASSERT_EQ(memcmp(&flags, &comparison, sizeof(flags)), 0);
> +
> +	flags = EMPTY_VMA_FLAGS;
> +	vma_flags_set(&flags, 64, 65);
> +
> +	/* Make sure we can clear a word without disturbing other bits. */
> +	vma_flags_set(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
> +	vma_flags_clear_word(&flags, VM_EXEC);
> +	ASSERT_EQ(memcmp(&flags, &comparison, sizeof(flags)), 0);
> +
> +	return true;
> +}
> +
> +/* Ensure that vma_flags_test() and friends works correctly. */
> +static bool test_vma_flags_test(void)
> +{
> +	const vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
> +					       VMA_EXEC_BIT, 64, 65);
> +	struct vm_area_struct vma;
> +	struct vm_area_desc desc;
> +
> +	vma.flags = flags;
> +	desc.vma_flags = flags;
> +
> +#define do_test(...)						\
> +	ASSERT_TRUE(vma_flags_test(&flags, __VA_ARGS__));	\
> +	ASSERT_TRUE(vma_desc_test_flags(&desc, __VA_ARGS__))
> +
> +#define do_test_all_true(...)					\
> +	ASSERT_TRUE(vma_flags_test_all(&flags, __VA_ARGS__));	\
> +	ASSERT_TRUE(vma_test_all_flags(&vma, __VA_ARGS__))
> +
> +#define do_test_all_false(...)					\
> +	ASSERT_FALSE(vma_flags_test_all(&flags, __VA_ARGS__));	\
> +	ASSERT_FALSE(vma_test_all_flags(&vma, __VA_ARGS__))
> +
> +	/*
> +	 * Testing for some flags that are present, some that are not - should
> +	 * pass. ANY flags matching should work.
> +	 */
> +	do_test(VMA_READ_BIT, VMA_MAYREAD_BIT, VMA_SEQ_READ_BIT);
> +	/* However, the ...test_all() variant should NOT pass. */
> +	do_test_all_false(VMA_READ_BIT, VMA_MAYREAD_BIT, VMA_SEQ_READ_BIT);
> +	/* But should pass for flags present. */
> +	do_test_all_true(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64, 65);
> +	/* Also subsets... */
> +	do_test_all_true(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64);
> +	do_test_all_true(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
> +	do_test_all_true(VMA_READ_BIT, VMA_WRITE_BIT);
> +	do_test_all_true(VMA_READ_BIT);
> +	/*
> +	 * Check _mask variant. We don't need to test extensively as macro
> +	 * helper is the equivalent.
> +	 */
> +	ASSERT_TRUE(vma_flags_test_mask(&flags, flags));
> +	ASSERT_TRUE(vma_flags_test_all_mask(&flags, flags));
> +
> +	/* Single bits. */
> +	do_test(VMA_READ_BIT);
> +	do_test(VMA_WRITE_BIT);
> +	do_test(VMA_EXEC_BIT);
> +#if NUM_VMA_FLAG_BITS > 64
> +	do_test(64);
> +	do_test(65);
> +#endif
> +
> +	/* Two bits. */
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT);
> +	do_test(VMA_READ_BIT, VMA_EXEC_BIT);
> +	do_test(VMA_WRITE_BIT, VMA_EXEC_BIT);
> +	/* Ordering shouldn't matter. */
> +	do_test(VMA_WRITE_BIT, VMA_READ_BIT);
> +	do_test(VMA_EXEC_BIT, VMA_READ_BIT);
> +	do_test(VMA_EXEC_BIT, VMA_WRITE_BIT);
> +#if NUM_VMA_FLAG_BITS > 64
> +	do_test(VMA_READ_BIT, 64);
> +	do_test(VMA_WRITE_BIT, 64);
> +	do_test(64, VMA_READ_BIT);
> +	do_test(64, VMA_WRITE_BIT);
> +	do_test(VMA_READ_BIT, 65);
> +	do_test(VMA_WRITE_BIT, 65);
> +	do_test(65, VMA_READ_BIT);
> +	do_test(65, VMA_WRITE_BIT);
> +#endif
> +	/* Three bits. */
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
> +#if NUM_VMA_FLAG_BITS > 64
> +	/* No need to consider every single permutation. */
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, 64);
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, 65);
> +
> +	/* Four bits. */
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64);
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 65);
> +
> +	/* Five bits. */
> +	do_test(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64, 65);
> +#endif
> +
> +#undef do_test
> +#undef do_test_all_true
> +#undef do_test_all_false
> +
> +	return true;
> +}
> +
> +/* Ensure that vma_flags_clear() and friends works correctly. */
> +static bool test_vma_flags_clear(void)
> +{
> +	vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
> +					 VMA_EXEC_BIT, 64, 65);
> +	vma_flags_t mask = mk_vma_flags(VMA_EXEC_BIT, 64);
> +	struct vm_area_struct vma;
> +	struct vm_area_desc desc;
> +
> +	vma.flags = flags;
> +	desc.vma_flags = flags;
> +
> +	/* Cursory check of _mask() variant, as the helper macros imply. */
> +	vma_flags_clear_mask(&flags, mask);
> +	vma_flags_clear_mask(&vma.flags, mask);
> +	vma_desc_clear_flags_mask(&desc, mask);
> +	ASSERT_FALSE(vma_flags_test(&flags, VMA_EXEC_BIT, 64));
> +	ASSERT_FALSE(vma_flags_test(&vma.flags, VMA_EXEC_BIT, 64));
> +	ASSERT_FALSE(vma_desc_test_flags(&desc, VMA_EXEC_BIT, 64));
> +	/* Reset. */
> +	vma_flags_set(&flags, VMA_EXEC_BIT, 64);
> +	vma_set_flags(&vma, VMA_EXEC_BIT, 64);
> +	vma_desc_set_flags(&desc, VMA_EXEC_BIT, 64);
> +
> +	/*
> +	 * Clear the flags and assert clear worked, then reset flags back to
> +	 * include specified flags.
> +	 */
> +#define do_test_and_reset(...)					\
> +	vma_flags_clear(&flags, __VA_ARGS__);			\
> +	vma_flags_clear(&vma.flags, __VA_ARGS__);		\
> +	vma_desc_clear_flags(&desc, __VA_ARGS__);		\
> +	ASSERT_FALSE(vma_flags_test(&flags, __VA_ARGS__));	\
> +	ASSERT_FALSE(vma_flags_test(&vma.flags, __VA_ARGS__));	\
> +	ASSERT_FALSE(vma_desc_test_flags(&desc, __VA_ARGS__));	\
> +	vma_flags_set(&flags, __VA_ARGS__);			\
> +	vma_set_flags(&vma, __VA_ARGS__);			\
> +	vma_desc_set_flags(&desc, __VA_ARGS__)
> +
> +	/* Single flags. */
> +	do_test_and_reset(VMA_READ_BIT);
> +	do_test_and_reset(VMA_WRITE_BIT);
> +	do_test_and_reset(VMA_EXEC_BIT);
> +	do_test_and_reset(64);
> +	do_test_and_reset(65);
> +
> +	/* Two flags, in different orders. */
> +	do_test_and_reset(VMA_READ_BIT, VMA_WRITE_BIT);
> +	do_test_and_reset(VMA_READ_BIT, VMA_EXEC_BIT);
> +	do_test_and_reset(VMA_READ_BIT, 64);
> +	do_test_and_reset(VMA_READ_BIT, 65);
> +	do_test_and_reset(VMA_WRITE_BIT, VMA_READ_BIT);
> +	do_test_and_reset(VMA_WRITE_BIT, VMA_EXEC_BIT);
> +	do_test_and_reset(VMA_WRITE_BIT, 64);
> +	do_test_and_reset(VMA_WRITE_BIT, 65);
> +	do_test_and_reset(VMA_EXEC_BIT, VMA_READ_BIT);
> +	do_test_and_reset(VMA_EXEC_BIT, VMA_WRITE_BIT);
> +	do_test_and_reset(VMA_EXEC_BIT, 64);
> +	do_test_and_reset(VMA_EXEC_BIT, 65);
> +	do_test_and_reset(64, VMA_READ_BIT);
> +	do_test_and_reset(64, VMA_WRITE_BIT);
> +	do_test_and_reset(64, VMA_EXEC_BIT);
> +	do_test_and_reset(64, 65);
> +	do_test_and_reset(65, VMA_READ_BIT);
> +	do_test_and_reset(65, VMA_WRITE_BIT);
> +	do_test_and_reset(65, VMA_EXEC_BIT);
> +	do_test_and_reset(65, 64);
> +
> +	/* Three flags. */
> +
> +#undef do_test_some_missing
> +#undef do_test_and_reset
> +
> +	return true;
> +}
> +
>  static void run_vma_tests(int *num_tests, int *num_fail)
>  {
>  	TEST(copy_vma);
> +	TEST(vma_flags_unchanged);
> +	TEST(vma_flags_cleared);
> +	TEST(vma_flags_word);
> +	TEST(vma_flags_test);
> +	TEST(vma_flags_clear);
>  }
> diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
> index e3ed05b57819..0e1121e2ef23 100644
> --- a/tools/testing/vma/vma_internal.h
> +++ b/tools/testing/vma/vma_internal.h
> @@ -36,11 +36,11 @@
>   * ahead of all other headers.
>   */
>  #define __private
> -#define NUM_MM_FLAG_BITS (64)
> +/* NUM_MM_FLAG_BITS defined by test code. */
>  typedef struct {
>  	__private DECLARE_BITMAP(__mm_flags, NUM_MM_FLAG_BITS);
>  } mm_flags_t;
> -#define NUM_VMA_FLAG_BITS BITS_PER_LONG
> +/* NUM_VMA_FLAG_BITS defined by test code. */
>  typedef struct {
>  	DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS);
>  } __private vma_flags_t;
> -- 
> 2.52.0
> 

^ permalink raw reply

* Re: [PATCH v2 1/3] integrity: Make arch_ima_get_secureboot integrity-wide
From: Mimi Zohar @ 2026-02-09 20:43 UTC (permalink / raw)
  To: Coiby Xu, linux-integrity
  Cc: Heiko Carstens, Alexander Egorenkov, Ard Biesheuvel, Dave Hansen,
	Roberto Sassu, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), Vasily Gorbik,
	Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin,
	Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
	Serge E. Hallyn, Jarkko Sakkinen, open list,
	open list:LINUX FOR POWERPC (32-BIT AND 64-BIT),
	open list:S390 ARCHITECTURE,
	open list:EXTENSIBLE FIRMWARE INTERFACE (EFI),
	open list:SECURITY SUBSYSTEM, open list:KEYS/KEYRINGS_INTEGRITY
In-Reply-To: <20260203041434.872784-2-coxu@redhat.com>

On Tue, 2026-02-03 at 12:14 +0800, Coiby Xu wrote:
> EVM and other LSMs need the ability to query the secure boot status of
> the system, without directly calling the IMA arch_ima_get_secureboot
> function. Refactor the secure boot status check into a general function
> named arch_get_secureboot.
> 
> Reported-and-suggested-by: Mimi Zohar <zohar@linux.ibm.com>
> Suggested-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: Coiby Xu <coxu@redhat.com>

Thanks, Coiby.  Other than unnecessarily splitting a line, the patch set looks
good.  As soon as the open window closes, I'll queue these patches for linux-
next.

> diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
> index 138029bfcce1..27521d665d33 100644
> --- a/security/integrity/ima/ima_efi.c
> +++ b/security/integrity/ima/ima_efi.c
> @@ -2,52 +2,9 @@
>  /*
>   * Copyright (C) 2018 IBM Corporation
>   */
> -#include <linux/efi.h>
>  #include <linux/module.h>
>  #include <linux/ima.h>
> -#include <asm/efi.h>
> -
> -#ifndef arch_ima_efi_boot_mode
> -#define arch_ima_efi_boot_mode efi_secureboot_mode_unset
> -#endif
> -
> -static enum efi_secureboot_mode get_sb_mode(void)
> -{
> -	enum efi_secureboot_mode mode;
> -
> -	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
> -		pr_info("ima: secureboot mode unknown, no efi\n");
> -		return efi_secureboot_mode_unknown;
> -	}
> -
> -	mode = efi_get_secureboot_mode(efi.get_variable);
> -	if (mode == efi_secureboot_mode_disabled)
> -		pr_info("ima: secureboot mode disabled\n");
> -	else if (mode == efi_secureboot_mode_unknown)
> -		pr_info("ima: secureboot mode unknown\n");
> -	else
> -		pr_info("ima: secureboot mode enabled\n");
> -	return mode;
> -}
> -
> -bool arch_ima_get_secureboot(void)
> -{
> -	static enum efi_secureboot_mode sb_mode;
> -	static bool initialized;
> -
> -	if (!initialized && efi_enabled(EFI_BOOT)) {
> -		sb_mode = arch_ima_efi_boot_mode;
> -
> -		if (sb_mode == efi_secureboot_mode_unset)
> -			sb_mode = get_sb_mode();
> -		initialized = true;
> -	}
> -
> -	if (sb_mode == efi_secureboot_mode_enabled)
> -		return true;
> -	else
> -		return false;
> -}
> +#include <linux/secure_boot.h>
>  
>  /* secureboot arch rules */
>  static const char * const sb_arch_rules[] = {
> @@ -67,7 +24,8 @@ static const char * const sb_arch_rules[] = {
>  
>  const char * const *arch_get_ima_policy(void)
>  {
> -	if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
> +	if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) &&
> +	    arch_get_secureboot()) {

No need to split the line here or below.


>  		if (IS_ENABLED(CONFIG_MODULE_SIG))
>  			set_module_sig_enforced();
>  		if (IS_ENABLED(CONFIG_KEXEC_SIG))
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 5770cf691912..6d093ac82a45 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -949,8 +949,8 @@ static int ima_load_data(enum kernel_load_data_id id, bool contents)
>  
>  	switch (id) {
>  	case LOADING_KEXEC_IMAGE:
> -		if (IS_ENABLED(CONFIG_KEXEC_SIG)
> -		    && arch_ima_get_secureboot()) {
> +		if (IS_ENABLED(CONFIG_KEXEC_SIG) &&
> +		    arch_get_secureboot()) {

===>

Mimi

>  			pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
>  			return -EACCES;
>  		}

^ permalink raw reply

* [PATCH v6 0/3] ima: Detect changes to files via kstat changes rather than i_version
From: Frederick Lawler @ 2026-02-09 21:21 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
	Christian Brauner, Josef Bacik, Jeff Layton
  Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
	Frederick Lawler

We uncovered a case in kernels >= 6.13 where XFS is no longer updating
struct kstat.change_cookie on i_op getattr() access calls. Instead, XFS is
using multigrain ctime (as well as other file systems) for
change detection in commit 1cf7e834a6fb ("xfs: switch to
multigrain timestamps").

Because file systems may implement i_version as they see fit, IMA
unnecessarily measures files.

We're proposing to compare against the kstat.change_cookie
directly to the cached version, and fall back to a ctime comparison,
if STATX_CHANGE_COOKIE is not supplied by vfs_getattr_nosec()'s result
mask.

EVM is largely left alone since there's no trivial way to query a file
directly in the LSM call paths to obtain kstat.change_cookie &
kstat.ctime to cache. Thus retains accessing i_version directly.

Regression tests will be added to the Linux Test Project instead of
selftest to help catch future file system changes that may impact
future evaluation of IMA.

I'd like this to be backported to at least 6.18 if possible.

Patches 1 & 2 are preparation patches. Ideally patch 2 is squashed into
3, though not strictly necessary.

Below is a simplified test that demonstrates the issue such that
there are multiple unnecessary measurements occurring for actions on
a file in a stacked TMPFS on XFS, prior to the file moved over to TMPFS:

_fragment.config_
CONFIG_XFS_FS=y
CONFIG_OVERLAY_FS=y
CONFIG_IMA=y
CONFIG_IMA_WRITE_POLICY=y
CONFIG_IMA_READ_POLICY=y

_./test.sh_

IMA_POLICY="/sys/kernel/security/ima/policy"
TEST_BIN="/bin/date"
MNT_BASE="/tmp/ima_test_root"

mkdir -p "$MNT_BASE"
mount -t tmpfs tmpfs "$MNT_BASE"
mkdir -p "$MNT_BASE"/{xfs_disk,upper,work,ovl}

dd if=/dev/zero of="$MNT_BASE/xfs.img" bs=1M count=300
mkfs.xfs -q "$MNT_BASE/xfs.img"
mount "$MNT_BASE/xfs.img" "$MNT_BASE/xfs_disk"
cp "$TEST_BIN" "$MNT_BASE/xfs_disk/test_prog"

mount -t overlay overlay -o \
"lowerdir=$MNT_BASE/xfs_disk,upperdir=$MNT_BASE/upper,workdir=$MNT_BASE/work" \
"$MNT_BASE/ovl"

echo "audit func=BPRM_CHECK uid=$(id -u nobody)" > "$IMA_POLICY"

target_prog="$MNT_BASE/ovl/test_prog"
setpriv --reuid nobody "$target_prog"
setpriv --reuid nobody "$target_prog"
setpriv --reuid nobody "$target_prog"

audit_count=$(dmesg | grep -c "file=\"$target_prog\"")

if [[ "$audit_count" -eq 1 ]]; then
        echo "PASS: Found exactly 1 audit event."
else
        echo "FAIL: Expected 1 audit event, but found $audit_count."
        exit 1
fi

Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
Changes in v6:
- Patch 1: No changes
- All other patches including cover letter have descriptions updated.
- Link to v5: https://lore.kernel.org/r/20260130-xfs-ima-fixup-v5-0-57e84ea91712@cloudflare.com

Changes in v5:
- Split into patch series. [Mimi]
- Link to v4: https://lore.kernel.org/r/20260129-xfs-ima-fixup-v4-1-6bb89df7b6a3@cloudflare.com

Changes in v4:
- No functional changes.
- Add Reviewed-by & Fixes tags.
- Link to v3: https://lore.kernel.org/r/20260122-xfs-ima-fixup-v3-1-20335a8aa836@cloudflare.com

Changes in v3:
- Prefer timespec64_to_ns() to leverage attr.version. [Roberto]
- s/TPMFS/TMPFS/ in description.
- Link to v2: https://lore.kernel.org/r/20260120-xfs-ima-fixup-v2-1-f332ead8b043@cloudflare.com

Changes in v2:
- Updated commit description + message to clarify the problem.
- compare struct timespec64 to avoid collision possibility [Roberto].
- Don't check inode_attr_changed() in ima_check_last_writer()
- Link to v1: https://lore.kernel.org/r/20260112-xfs-ima-fixup-v1-1-8d13b6001312@cloudflare.com

Changes since RFC:
- Remove calls to I_IS_VERSION()
- Function documentation/comments
- Abide IMA/EVM change detection fallback invariants
- Combined ctime guard into version for attributes struct
- Link to RFC: https://lore.kernel.org/r/20251229-xfs-ima-fixup-v1-1-6a717c939f7c@cloudflare.com

---
Frederick Lawler (3):
      ima: Unify vfs_getattr_nosec() stat comparisons under helper function
      ima: Make integrity_inode_attrs_changed() call into VFS
      ima: Use kstat.ctime as a fallback for change detection

 include/linux/integrity.h         | 43 +++++++++++++++++++++++++++++++++++----
 security/integrity/evm/evm_main.c |  5 ++---
 security/integrity/ima/ima_api.c  | 11 +++++++---
 security/integrity/ima/ima_main.c | 11 +++++-----
 4 files changed, 54 insertions(+), 16 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251212-xfs-ima-fixup-931780a62c2c

Best regards,
-- 
Frederick Lawler <fred@cloudflare.com>


^ permalink raw reply

* [PATCH v6 1/3] ima: Unify vfs_getattr_nosec() stat comparisons under helper function
From: Frederick Lawler @ 2026-02-09 21:21 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
	Christian Brauner, Josef Bacik, Jeff Layton
  Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
	Frederick Lawler
In-Reply-To: <20260209-xfs-ima-fixup-v6-0-72f576f90e67@cloudflare.com>

The logic for comparing kstat.change_cookie against IMA version is
hard to read. Abstract comparison logic into a new function
integrity_inode_attrs_stat_changed().

No functional change intended.

Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
 include/linux/integrity.h         | 11 +++++++++++
 security/integrity/ima/ima_main.c |  4 ++--
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index f5842372359be5341b6870a43b92e695e8fc78af..beb9ab19fa6257e79266b58bcb5f55b0c5445828 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -49,6 +49,17 @@ integrity_inode_attrs_store(struct integrity_inode_attributes *attrs,
 	attrs->ino = inode->i_ino;
 }
 
+/* Compares stat attributes for change detection. */
+static inline bool
+integrity_inode_attrs_stat_changed
+(const struct integrity_inode_attributes *attrs, const struct kstat *stat)
+{
+	if (stat->result_mask & STATX_CHANGE_COOKIE)
+		return stat->change_cookie != attrs->version;
+
+	return true;
+}
+
 /*
  * On stacked filesystems detect whether the inode or its content has changed.
  */
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5770cf691912aa912fc65280c59f5baac35dd725..6570ad10887b9ea1172c78274cf62482350e87ff 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -201,8 +201,8 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 		    vfs_getattr_nosec(&file->f_path, &stat,
 				      STATX_CHANGE_COOKIE,
 				      AT_STATX_SYNC_AS_STAT) ||
-		    !(stat.result_mask & STATX_CHANGE_COOKIE) ||
-		    stat.change_cookie != iint->real_inode.version) {
+		    integrity_inode_attrs_stat_changed(&iint->real_inode,
+						       &stat)) {
 			iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
 			iint->measured_pcrs = 0;
 			if (update)

-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 2/3] ima: Make integrity_inode_attrs_changed() call into VFS
From: Frederick Lawler @ 2026-02-09 21:21 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
	Christian Brauner, Josef Bacik, Jeff Layton
  Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
	Frederick Lawler
In-Reply-To: <20260209-xfs-ima-fixup-v6-0-72f576f90e67@cloudflare.com>

ima_check_last_writer() is already comparing kstat.change_cookie
against the cached attribute's version set by
ima_collect_masurement(). Make integrity_inode_attrs_changed()
call into the VFS as well. For file systems that do not set the
change cookie, IMA still treats the file as changed.

Fall back to current behavior if VFS cannot obtain it.

No logical change intended.

Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
 include/linux/integrity.h         | 28 ++++++++++++++++++++++++----
 security/integrity/evm/evm_main.c |  5 ++---
 security/integrity/ima/ima_main.c |  5 ++---
 3 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index beb9ab19fa6257e79266b58bcb5f55b0c5445828..382c783f0fa3ae4a938cdf9559291ba1903a378e 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -9,6 +9,7 @@
 
 #include <linux/fs.h>
 #include <linux/iversion.h>
+#include <linux/kernel.h>
 
 enum integrity_status {
 	INTEGRITY_PASS = 0,
@@ -62,14 +63,33 @@ integrity_inode_attrs_stat_changed
 
 /*
  * On stacked filesystems detect whether the inode or its content has changed.
+ *
+ * Must be called in process context.
  */
 static inline bool
 integrity_inode_attrs_changed(const struct integrity_inode_attributes *attrs,
-			      const struct inode *inode)
+			      struct file *file, struct inode *inode)
 {
-	return (inode->i_sb->s_dev != attrs->dev ||
-		inode->i_ino != attrs->ino ||
-		!inode_eq_iversion(inode, attrs->version));
+	struct kstat stat;
+
+	might_sleep();
+
+	if (inode->i_sb->s_dev != attrs->dev || inode->i_ino != attrs->ino)
+		return true;
+
+	/*
+	 * EVM currently relies on backing inode i_version. While IS_I_VERSION
+	 * is not a good indicator of i_version support, this still retains
+	 * the logic such that a re-evaluation should still occur for EVM, and
+	 * only for IMA if vfs_getattr_nosec() fails.
+	 */
+	if (!file || vfs_getattr_nosec(&file->f_path, &stat,
+				       STATX_CHANGE_COOKIE,
+				       AT_STATX_SYNC_AS_STAT))
+		return !IS_I_VERSION(inode) ||
+		       !inode_eq_iversion(inode, attrs->version);
+
+	return integrity_inode_attrs_stat_changed(attrs, &stat);
 }
 
 
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 73d500a375cb37a54f295b0e1e93fd6e5d9ecddc..6a4e0e246005246d5700b1db590c1759242b9cb6 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -752,9 +752,8 @@ bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode)
 	bool ret = false;
 
 	if (iint) {
-		ret = (!IS_I_VERSION(metadata_inode) ||
-		       integrity_inode_attrs_changed(&iint->metadata_inode,
-						     metadata_inode));
+		ret = integrity_inode_attrs_changed(&iint->metadata_inode,
+						    NULL, metadata_inode);
 		if (ret)
 			iint->evm_status = INTEGRITY_UNKNOWN;
 	}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 6570ad10887b9ea1172c78274cf62482350e87ff..8cb17c9d446caaa5a98f5ec8f027c17ba7babca8 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -328,9 +328,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	real_inode = d_real_inode(file_dentry(file));
 	if (real_inode != inode &&
 	    (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
-		if (!IS_I_VERSION(real_inode) ||
-		    integrity_inode_attrs_changed(&iint->real_inode,
-						  real_inode)) {
+		if (integrity_inode_attrs_changed(&iint->real_inode,
+						  file, real_inode)) {
 			iint->flags &= ~IMA_DONE_MASK;
 			iint->measured_pcrs = 0;
 		}

-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 3/3] ima: Use kstat.ctime as a fallback for change detection
From: Frederick Lawler @ 2026-02-09 21:21 UTC (permalink / raw)
  To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
	Christian Brauner, Josef Bacik, Jeff Layton
  Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
	Frederick Lawler
In-Reply-To: <20260209-xfs-ima-fixup-v6-0-72f576f90e67@cloudflare.com>

IMA performs unnecessary measurements on files in file systems
that do not set STATX_CHANGE_COOKIE in the vfs_getattr_nosec()'s
result mask.

Commit 1cf7e834a6fb ("xfs: switch to multigrain timestamps") introduced
multigrain timestamps to XFS, and this made XFS no longer report an
inode's i_version used by ima_collect_measurement() for change detection.

Additionally, ima_check_last_writer() & integrity_inode_attributes_change()
think there's a change for a file, when there may have not been.

Most file systems already use ctime for change detection, therefore, make
IMA fall back to compare against ctime when a file system does not report
an i_version to the kstat.change_cookie.

If neither fields are reported by vfs_getattr_nosec(), assume the file
changed.

timespec64_to_ns() is chosen to avoid adding extra storage to
integrity_inode_attributes by leveraging the existing version field.

XFS is just one example. There may be more file systems that adopt
multigrain timestamps in the future.

Link: https://lore.kernel.org/all/aTspr4_h9IU4EyrR@CMGLRV3
Fixes: 1cf7e834a6fb ("xfs: switch to multigrain timestamps")
Suggested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
 include/linux/integrity.h         |  6 +++++-
 security/integrity/ima/ima_api.c  | 11 ++++++++---
 security/integrity/ima/ima_main.c |  2 +-
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 382c783f0fa3ae4a938cdf9559291ba1903a378e..ec2c94907f417c4a71ecce29ac79edac9bc2c6f8 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -10,6 +10,7 @@
 #include <linux/fs.h>
 #include <linux/iversion.h>
 #include <linux/kernel.h>
+#include <linux/time64.h>
 
 enum integrity_status {
 	INTEGRITY_PASS = 0,
@@ -58,6 +59,9 @@ integrity_inode_attrs_stat_changed
 	if (stat->result_mask & STATX_CHANGE_COOKIE)
 		return stat->change_cookie != attrs->version;
 
+	if (stat->result_mask & STATX_CTIME)
+		return timespec64_to_ns(&stat->ctime) != (s64)attrs->version;
+
 	return true;
 }
 
@@ -84,7 +88,7 @@ integrity_inode_attrs_changed(const struct integrity_inode_attributes *attrs,
 	 * only for IMA if vfs_getattr_nosec() fails.
 	 */
 	if (!file || vfs_getattr_nosec(&file->f_path, &stat,
-				       STATX_CHANGE_COOKIE,
+				       STATX_CHANGE_COOKIE | STATX_CTIME,
 				       AT_STATX_SYNC_AS_STAT))
 		return !IS_I_VERSION(inode) ||
 		       !inode_eq_iversion(inode, attrs->version);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c35ea613c9f8d404ba4886e3b736c3bab29d1668..e47d6281febc15a0ac1bd2ea1d28fea4d0cd5c58 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -272,10 +272,15 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
 	 * to an initial measurement/appraisal/audit, but was modified to
 	 * assume the file changed.
 	 */
-	result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
+	result = vfs_getattr_nosec(&file->f_path, &stat,
+				   STATX_CHANGE_COOKIE | STATX_CTIME,
 				   AT_STATX_SYNC_AS_STAT);
-	if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
-		i_version = stat.change_cookie;
+	if (!result) {
+		if (stat.result_mask & STATX_CHANGE_COOKIE)
+			i_version = stat.change_cookie;
+		else if (stat.result_mask & STATX_CTIME)
+			i_version = timespec64_to_ns(&stat.ctime);
+	}
 	hash.hdr.algo = algo;
 	hash.hdr.length = hash_digest_size[algo];
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 8cb17c9d446caaa5a98f5ec8f027c17ba7babca8..776db158b0bd8a0d053729ac0cc15af8b6020a98 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -199,7 +199,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 					    &iint->atomic_flags);
 		if ((iint->flags & IMA_NEW_FILE) ||
 		    vfs_getattr_nosec(&file->f_path, &stat,
-				      STATX_CHANGE_COOKIE,
+				      STATX_CHANGE_COOKIE | STATX_CTIME,
 				      AT_STATX_SYNC_AS_STAT) ||
 		    integrity_inode_attrs_stat_changed(&iint->real_inode,
 						       &stat)) {

-- 
2.43.0


^ permalink raw reply related

* [GIT PULL] Smack patches for 7.0
From: Casey Schaufler @ 2026-02-09 21:15 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: LSM List, Linux kernel mailing list, Konstantin Andreev,
	Taimoor Zaeem, Casey Schaufler
In-Reply-To: <28dea16f-18c3-4a2f-821c-e85b660125ed.ref@schaufler-ca.com>

Hello Linus,

Here is the Smack pull request for v7.0.

There's nothing exciting. Two improvements to the code for setting
the CIPSO Domain Of Interpretation (DOI), a seldom used feature,
and a formatting change. All tested and included in next for some time.

The following changes since commit f8f9c1f4d0c7a64600e2ca312dec824a0bc2f1da:

  Linux 6.19-rc3 (2025-12-28 13:24:26 -0800)

are available in the Git repository at:

  https://github.com/cschaufler/smack-next tags/Smack-for-7.0

for you to fetch changes up to 33d589ed60ae433b483761987b85e0d24e54584e:

  smack: /smack/doi: accept previously used values (2025-12-30 12:17:15 -0800)

----------------------------------------------------------------
Smack fixes for DOI specification.

----------------------------------------------------------------
Konstantin Andreev (2):
      smack: /smack/doi must be > 0
      smack: /smack/doi: accept previously used values

Taimoor Zaeem (1):
      security: smack: fix indentation in smack_access.c

 security/smack/smack_access.c |  2 +-
 security/smack/smackfs.c      | 79 +++++++++++++++++++++++++++----------------
 2 files changed, 51 insertions(+), 30 deletions(-)


^ permalink raw reply

* Re: [GIT PULL] selinux/selinux-pr-20260203
From: pr-tracker-bot @ 2026-02-10  0:50 UTC (permalink / raw)
  To: Paul Moore; +Cc: Linus Torvalds, selinux, linux-security-module, linux-kernel
In-Reply-To: <74f395ba13926ab0391bd8714abc6036@paul-moore.com>

The pull request you sent on Tue, 03 Feb 2026 23:10:38 -0500:

> https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git tags/selinux-pr-20260203

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/6252e917b9006dfa2f3d884fe0dbaf3e676c4108

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* Re: [GIT PULL] lsm/lsm-pr-20260203
From: pr-tracker-bot @ 2026-02-10  0:50 UTC (permalink / raw)
  To: Paul Moore; +Cc: Linus Torvalds, linux-security-module, linux-kernel
In-Reply-To: <d6f1f788f25b30ddc05703b97146f6ad@paul-moore.com>

The pull request you sent on Tue, 03 Feb 2026 23:10:44 -0500:

> https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm.git tags/lsm-pr-20260203

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/bcc8fd3e1573c502edc0cb61abea0e113a761799

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* Re: [PATCH 1/5] export file_close_fd and task_work_add
From: Alice Ryhl @ 2026-02-10  8:47 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Greg Kroah-Hartman, Carlos Llamas, Alexander Viro,
	Christian Brauner, Jan Kara, Paul Moore, James Morris,
	Serge E. Hallyn, Andrew Morton, Dave Chinner, Qi Zheng,
	Roman Gushchin, Muchun Song, David Hildenbrand, Liam R. Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	kernel-team, linux-fsdevel, linux-kernel, linux-security-module,
	linux-mm, rust-for-linux
In-Reply-To: <df876a6e-013c-4566-890d-7c1d662fced3@lucifer.local>

On Mon, Feb 09, 2026 at 03:21:58PM +0000, Lorenzo Stoakes wrote:
> On Thu, Feb 05, 2026 at 01:45:40PM +0000, Alice Ryhl wrote:
> > +/**
> > + * close_fd_safe - close the given fd
> > + * @fd: file descriptor to close
> > + * @flags: gfp flags for allocation of task work
> > + *
> > + * This closes an fd. Unlike close_fd(), this may be used even if the fd is
> > + * currently held with fdget().
> > + *
> > + * Returns: 0 or an error code
> > + */
> > +int close_fd_safe(unsigned int fd, gfp_t flags)
> > +{
> > +	struct close_fd_safe_task_work *twcb;
> > +
> > +	twcb = kzalloc(sizeof(*twcb), flags);
> > +	if (!twcb)
> > +		return -ENOMEM;
> > +	init_task_work(&twcb->twork, close_fd_safe_callback);
> > +	twcb->file = file_close_fd(fd);
> > +	if (!twcb->file) {
> > +		kfree(twcb);
> > +		return -EBADF;
> > +	}
> > +
> > +	get_file(twcb->file);
> > +	filp_close(twcb->file, current->files);
> > +	task_work_add(current, &twcb->twork, TWA_RESUME);
> > +	return 0;
> > +}
> 
> Would need an EXPORT_SYMBOL_FOR_MODULES(...) here right?

Ah yeah, for Binder to become a module it would need to be exported.
(Though maybe it's worth moving this logic even if Binder is not made
into a module?)

> > diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
> > index c45306a9f007..1c99a56c0cdf 100644
> > --- a/include/linux/fdtable.h
> > +++ b/include/linux/fdtable.h
> > @@ -111,6 +111,7 @@ int iterate_fd(struct files_struct *, unsigned,
> >  		const void *);
> >
> >  extern int close_fd(unsigned int fd);
> > +extern int close_fd_safe(unsigned int fd, gfp_t flags);
> 
> One nit, generally well in mm anyway we avoid the 'extern' and remove them as we
> go. Not sure about vfs actually though?

Right. Not sure about this.

> >  extern struct file *file_close_fd(unsigned int fd);
> >
> >  extern struct kmem_cache *files_cachep;
> 
> I mean this is essentially taking what's in binder and making it a general
> thing, so needs Christian's input on whether this is sensible I think :)

Yeah.

Alice

^ permalink raw reply

* Re: [PATCH v4 1/6] lsm: Add LSM hook security_unix_find
From: Justin Suess @ 2026-02-10 13:02 UTC (permalink / raw)
  To: mic
  Cc: brauner, demiobenour, fahimitahera, gnoack3000, hi, horms,
	ivanov.mikhail1, jannh, jmorris, john.johansen,
	konstantin.meskhidze, linux-security-module, m, matthieu, netdev,
	paul, samasth.norway.ananda, serge, utilityemal77, viro
In-Reply-To: <20260209.yeeh3ieDuz9u@digikod.net>

> On Mon, Feb 09, 2026 at 12:10:11AM +0100, Günther Noack wrote:
> > From: Justin Suess <utilityemal77@gmail.com>
> > 
> > Add a LSM hook security_unix_find.
> > 
> > This hook is called to check the path of a named unix socket before a
> > connection is initiated. The peer socket may be inspected as well.
> > 
> > Why existing hooks are unsuitable:
> > 
> > Existing socket hooks, security_unix_stream_connect(),
> > security_unix_may_send(), and security_socket_connect() don't provide
> > TOCTOU-free / namespace independent access to the paths of sockets.
> > 
> > (1) We cannot resolve the path from the struct sockaddr in existing hooks.
> > This requires another path lookup. A change in the path between the
> > two lookups will cause a TOCTOU bug.
> > 
> > (2) We cannot use the struct path from the listening socket, because it
> > may be bound to a path in a different namespace than the caller,
> > resulting in a path that cannot be referenced at policy creation time.
> > 
> > Cc: Günther Noack <gnoack3000@gmail.com>
> > Cc: Tingmao Wang <m@maowtm.org>
> > Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> > ---
> >  include/linux/lsm_hook_defs.h |  5 +++++
> >  include/linux/security.h      | 11 +++++++++++
> >  net/unix/af_unix.c            |  9 +++++++++
> >  security/security.c           | 20 ++++++++++++++++++++
> >  4 files changed, 45 insertions(+)
> > 
> > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > index 8c42b4bde09c..7a0fd3dbfa29 100644
> > --- a/include/linux/lsm_hook_defs.h
> > +++ b/include/linux/lsm_hook_defs.h
> > @@ -317,6 +317,11 @@ LSM_HOOK(int, 0, post_notification, const struct cred *w_cred,
> >  LSM_HOOK(int, 0, watch_key, struct key *key)
> >  #endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
> >  
> > +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> > +LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other,
> > +	 int flags)
> > +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> > +
> >  #ifdef CONFIG_SECURITY_NETWORK
> >  LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other,
> >  	 struct sock *newsk)
> > diff --git a/include/linux/security.h b/include/linux/security.h
> > index 83a646d72f6f..99a33d8eb28d 100644
> > --- a/include/linux/security.h
> > +++ b/include/linux/security.h
> > @@ -1931,6 +1931,17 @@ static inline int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> >  }
> >  #endif	/* CONFIG_SECURITY_NETWORK */
> >  
> > +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> > +
> > +int security_unix_find(const struct path *path, struct sock *other, int flags);
> > +
> > +#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> > +static inline int security_unix_find(const struct path *path, struct sock *other, int flags)
> > +{
> > +	return 0;
> > +}
> > +#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> > +
> >  #ifdef CONFIG_SECURITY_INFINIBAND
> >  int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey);
> >  int security_ib_endport_manage_subnet(void *sec, const char *name, u8 port_num);
> > diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> > index d0511225799b..db9d279b3883 100644
> > --- a/net/unix/af_unix.c
> > +++ b/net/unix/af_unix.c
> > @@ -1226,10 +1226,19 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
> >  	if (!S_ISSOCK(inode->i_mode))
> >  		goto path_put;
> >  
> > +	err = -ECONNREFUSED;
> 
> We don't see it in this patch but err is already set to -ECONNREFUSED.
> This line might be confusing, and unrelated to the goal of this patch,
> so we should remove it.

Done. I debated keeping it, but it seems more appropriate to follow the
convention. Thanks for the catch.

> 
> 
> >  	sk = unix_find_socket_byinode(inode);
> >  	if (!sk)
> >  		goto path_put;
> >  
> > +	/*
> > +	 * We call the hook because we know that the inode is a socket
> > +	 * and we hold a valid reference to it via the path.
> 
> This comment can be alligned with 80 columns.

Done.

> > +	 */
> > +	err = security_unix_find(&path, sk, flags);
> 
> This hook makes sense and is quite generic.

Indeed, I suspect it will be useful for other path-based LSM.

> > +	if (err)
> > +		goto sock_put;
> > +
> >  	err = -EPROTOTYPE;
> >  	if (sk->sk_type == type)
> >  		touch_atime(&path);
> > diff --git a/security/security.c b/security/security.c
> > index 31a688650601..9e9515955098 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -4731,6 +4731,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
> >  
> >  #endif	/* CONFIG_SECURITY_NETWORK */
> >  
> > +#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
> > +/*
> 
> This should be a docstring like other hooks: /**

Done.

> 
> > + * security_unix_find() - Check if a named AF_UNIX socket can connect
> > + * @path: path of the socket being connected to
> > + * @other: peer sock
> > + * @flags: flags associated with the socket
> > + *
> > + * This hook is called to check permissions before connecting to a named
> > + * AF_UNIX socket.
> > + *
> > + * Return: Returns 0 if permission is granted.
> > + */
> > +int security_unix_find(const struct path *path, struct sock *other, int flags)
> > +{
> > +	return call_int_hook(unix_find, path, other, flags);
> > +}
> > +EXPORT_SYMBOL(security_unix_find);
> > +
> > +#endif	/* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
> > +
> >  #ifdef CONFIG_SECURITY_INFINIBAND
> >  /**
> >   * security_ib_pkey_access() - Check if access to an IB pkey is allowed
> > -- 
> > 2.52.0
> > 
> > 
>

Below follows revised lsm hook patch based on feedback.

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..7a0fd3dbfa29 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -317,6 +317,11 @@ LSM_HOOK(int, 0, post_notification, const struct cred *w_cred,
 LSM_HOOK(int, 0, watch_key, struct key *key)
 #endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
 
+#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
+LSM_HOOK(int, 0, unix_find, const struct path *path, struct sock *other,
+	 int flags)
+#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+
 #ifdef CONFIG_SECURITY_NETWORK
 LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other,
 	 struct sock *newsk)
diff --git a/include/linux/security.h b/include/linux/security.h
index 83a646d72f6f..99a33d8eb28d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1931,6 +1931,17 @@ static inline int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
 }
 #endif	/* CONFIG_SECURITY_NETWORK */
 
+#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
+
+int security_unix_find(const struct path *path, struct sock *other, int flags);
+
+#else /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+static inline int security_unix_find(const struct path *path, struct sock *other, int flags)
+{
+	return 0;
+}
+#endif /* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+
 #ifdef CONFIG_SECURITY_INFINIBAND
 int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey);
 int security_ib_endport_manage_subnet(void *sec, const char *name, u8 port_num);
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d0511225799b..369812b79dd8 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1230,6 +1230,14 @@ static struct sock *unix_find_bsd(struct sockaddr_un *sunaddr, int addr_len,
 	if (!sk)
 		goto path_put;
 
+	/*
+	 * We call the hook because we know that the inode is a socket and we
+	 * hold a valid reference to it via the path.
+	 */
+	err = security_unix_find(&path, sk, flags);
+	if (err)
+		goto sock_put;
+
 	err = -EPROTOTYPE;
 	if (sk->sk_type == type)
 		touch_atime(&path);
diff --git a/security/security.c b/security/security.c
index 31a688650601..eaf8f8fdf0c2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4731,6 +4731,26 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
 
 #endif	/* CONFIG_SECURITY_NETWORK */
 
+#if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_SECURITY_PATH)
+/**
+ * security_unix_find() - Check if a named AF_UNIX socket can connect
+ * @path: path of the socket being connected to
+ * @other: peer sock
+ * @flags: flags associated with the socket
+ *
+ * This hook is called to check permissions before connecting to a named
+ * AF_UNIX socket.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_unix_find(const struct path *path, struct sock *other, int flags)
+{
+	return call_int_hook(unix_find, path, other, flags);
+}
+EXPORT_SYMBOL(security_unix_find);
+
+#endif	/* CONFIG_SECURITY_NETWORK && CONFIG_SECURITY_PATH */
+
 #ifdef CONFIG_SECURITY_INFINIBAND
 /**
  * security_ib_pkey_access() - Check if access to an IB pkey is allowed

^ permalink raw reply related

* Re: [PATCH v3 1/3] selftests/landlock: Add filesystem access benchmark
From: Mickaël Salaün @ 2026-02-10 15:42 UTC (permalink / raw)
  To: Günther Noack
  Cc: linux-security-module, Tingmao Wang, Justin Suess,
	Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
	konstantin.meskhidze, Randy Dunlap
In-Reply-To: <20260206151154.97915-3-gnoack3000@gmail.com>

On Fri, Feb 06, 2026 at 04:11:53PM +0100, Günther Noack wrote:
> fs_bench benchmarks the performance of Landlock's path walk
> by exercising it in a scenario that amplifies Landlock's overhead:
> 
> * Create a large number of nested directories
> * Enforce a Landlock policy in which a rule is associated with each of
>   these subdirectories
> * Benchmark openat() applied to the deepest directory,
>   forcing Landlock to walk the entire path.
> 
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
>  tools/testing/selftests/landlock/.gitignore |   1 +
>  tools/testing/selftests/landlock/Makefile   |   1 +
>  tools/testing/selftests/landlock/fs_bench.c | 214 ++++++++++++++++++++
>  3 files changed, 216 insertions(+)
>  create mode 100644 tools/testing/selftests/landlock/fs_bench.c
> 
> diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
> index a820329cae0d..1974e17a2611 100644
> --- a/tools/testing/selftests/landlock/.gitignore
> +++ b/tools/testing/selftests/landlock/.gitignore
> @@ -1,4 +1,5 @@
>  /*_test
> +/fs_bench
>  /sandbox-and-launch
>  /true
>  /wait-pipe
> diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
> index 044b83bde16e..fc43225d319a 100644
> --- a/tools/testing/selftests/landlock/Makefile
> +++ b/tools/testing/selftests/landlock/Makefile

> +int main(int argc, char *argv[])
> +{
> +	bool use_landlock = true;
> +	size_t num_iterations = 100000;
> +	size_t num_subdirs = 10000;
> +	int c, curr, fd;
> +	struct tms start_time, end_time;
> +
> +	setbuf(stdout, NULL);
> +	while ((c = getopt(argc, argv, "hLd:n:")) != -1) {
> +		switch (c) {
> +		case 'h':
> +			usage(argv[0]);
> +			return EXIT_SUCCESS;
> +		case 'L':
> +			use_landlock = false;
> +			break;
> +		case 'd':
> +			num_subdirs = atoi(optarg);
> +			break;
> +		case 'n':
> +			num_iterations = atoi(optarg);
> +			break;
> +		default:
> +			usage(argv[0]);
> +			return EXIT_FAILURE;
> +		}
> +	}
> +
> +	printf("*** Benchmark ***\n");
> +	printf("%zu dirs, %zu iterations, %s landlock\n", num_subdirs,
> +	       num_iterations, use_landlock ? "with" : "without");
> +
> +	if (times(&start_time) == -1)
> +		err(1, "times");
> +
> +	curr = build_directory(num_subdirs, use_landlock);
> +
> +	for (int i = 0; i < num_iterations; i++) {
> +		fd = openat(curr, "file.txt", O_CREAT | O_TRUNC | O_WRONLY);

Some build environments complain that O_CREAT requires the fourth
openat argument to be set.  I set the mode to 0600.

> +		if (use_landlock) {
> +			if (fd == 0)
> +				errx(1, "openat succeeded, expected EACCES");
> +			if (errno != EACCES)
> +				err(1, "openat expected EACCES, but got");
> +		}
> +		if (fd != -1)
> +			close(fd);
> +	}
> +
> +	if (times(&end_time) == -1)
> +		err(1, "times");
> +
> +	printf("*** Benchmark concluded ***\n");
> +	printf("System: %ld clocks\n",
> +	       end_time.tms_stime - start_time.tms_stime);
> +	printf("User  : %ld clocks\n",
> +	       end_time.tms_utime - start_time.tms_utime);
> +	printf("Clocks per second: %ld\n", CLOCKS_PER_SEC);
> +
> +	close(curr);
> +
> +	remove_recursively(num_subdirs);
> +}
> -- 
> 2.52.0
> 
> 

^ permalink raw reply

* [apparmor][PATCH] apparmor: fix signedness bug in unpack_tags()
From: Massimiliano Pellizzer @ 2026-02-10 17:15 UTC (permalink / raw)
  To: john.johansen
  Cc: apparmor, linux-security-module, linux-kernel, dan.carpenter,
	Massimiliano Pellizzer
In-Reply-To: <aYmsYRgv3VClpkjX@stanley.mountain>

Smatch static checker warning:
    security/apparmor/policy_unpack.c:966 unpack_pdb()
    warn: unsigned 'unpack_tags(e, &pdb->tags, info)' is never less than zero.

unpack_tags() is declared with return type size_t (unsigned) but returns
negative errno values on failure. The caller in unpack_pdb() tests the
return with `< 0`, which is always false for an unsigned type, making
error handling dead code. Malformed tag data would be silently accepted
instead of causing a load failure.

Change return type of unpack_tags() from size_t to int to match the
functions's actual semantic.

Fixes: 3d28e2397af7 ("apparmor: add support loading per permission tagging")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Massimiliano Pellizzer <mpellizzer.dev@gmail.com>
---
 security/apparmor/policy_unpack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index e68adf39771f..dc908e1f5a88 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -835,7 +835,7 @@ static int unpack_tag_headers(struct aa_ext *e, struct aa_tags_struct *tags)
 }
 
 
-static size_t unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags,
+static int unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags,
 	const char **info)
 {
 	int error = -EPROTO;
-- 
2.51.0


^ permalink raw reply related

* [apparmor][PATCH] apparmor: fix incorrect success return value in unpack_tag_headers()
From: Massimiliano Pellizzer @ 2026-02-10 17:21 UTC (permalink / raw)
  To: john.johansen
  Cc: apparmor, linux-security-module, linux-kernel,
	Massimiliano Pellizzer

unpack_tag_headers() returns `true` (1) on success instead of 0.
Since it's caller unpack_tags() checks the return value with
`if (error)`, a non-zero success value is incorrectly treated as
a failure, causing tag header unpacking to always even if the data
is well-formed.

Change the success return in unpack_tag_headers() from `true` to 0.

Fixes: 3d28e2397af7 ("apparmor: add support loading per permission tagging")
Signed-off-by: Massimiliano Pellizzer <mpellizzer.dev@gmail.com>
---
 security/apparmor/policy_unpack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index dc908e1f5a88..221208788025 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -825,7 +825,7 @@ static int unpack_tag_headers(struct aa_ext *e, struct aa_tags_struct *tags)
 	tags->hdrs.size = size;
 	tags->hdrs.table = hdrs;
 	AA_DEBUG(DEBUG_UNPACK, "headers %ld size %d", (long) hdrs, size);
-	return true;
+	return 0;
 
 fail:
 	kfree_sensitive(hdrs);
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH v2 11/13] tools/testing/vma: separate VMA userland tests into separate files
From: Lorenzo Stoakes @ 2026-02-10 17:44 UTC (permalink / raw)
  To: Liam R. Howlett, Andrew Morton, Jarkko Sakkinen, Dave Hansen,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86,
	H . Peter Anvin, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Jani Nikula,
	Joonas Lahtinen, Rodrigo Vivi, Tvrtko Ursulin, Christian Koenig,
	Huang Rui, Matthew Auld, Matthew Brost, Alexander Viro,
	Christian Brauner, Jan Kara, Benjamin LaHaise, Gao Xiang, Chao Yu,
	Yue Hu, Jeffle Xu, Sandeep Dhavale, Hongbo Li, Chunhai Guo,
	Theodore Ts'o, Andreas Dilger, Muchun Song, Oscar Salvador,
	David Hildenbrand, Konstantin Komarov, Mike Marshall,
	Martin Brandenburg, Tony Luck, Reinette Chatre, Dave Martin,
	James Morse, Babu Moger, Carlos Maiolino, Damien Le Moal,
	Naohiro Aota, Johannes Thumshirn, Matthew Wilcox, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Hugh Dickins,
	Baolin Wang, Zi Yan, Nico Pache, Ryan Roberts, Dev Jain,
	Barry Song, Lance Yang, Jann Horn, Pedro Falcato, David Howells,
	Paul Moore, James Morris, Serge E . Hallyn, Yury Norov,
	Rasmus Villemoes, linux-sgx, linux-kernel, nvdimm, linux-cxl,
	dri-devel, intel-gfx, linux-fsdevel, linux-aio, linux-erofs,
	linux-ext4, linux-mm, ntfs3, devel, linux-xfs, keyrings,
	linux-security-module, Jason Gunthorpe
In-Reply-To: <opopo4rlyxz7upi2bzrx7e6cyji2nmaynvlhvr2nzvtvb5pfxu@4fekgb5faybz>

On Mon, Feb 09, 2026 at 07:58:22PM +0000, Liam R. Howlett wrote:
> * Lorenzo Stoakes <lorenzo.stoakes@oracle.com> [260122 16:06]:
> > So far the userland VMA tests have been established as a rough expression
> > of what's been possible.
> >
> > qAdapt it into a more usable form by separating out tests and shared helper
> ^^^^ Typo

Oops, Andrew - can you fix that up? Thanks! :)

>
>
> > functions.
> >
> > Since we test functions that are declared statically in mm/vma.c, we make
> > use of the trick of #include'ing kernel C files directly.
> >
> > In order for the tests to continue to function, we must therefore also
> > this way into the tests/ directory.
> >
> > We try to keep as much shared logic actually modularised into a separate
> > compilation unit in shared.c, however the merge_existing() and attach_vma()
> > helpers rely on statically declared mm/vma.c functions so these must be
> > declared in main.c.
> >
> > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
>
> Besides that typo, it looks good.
>
> Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>

Thanks :)

Cheers, Lorenzo

>
> > ---
> >  tools/testing/vma/Makefile                 |   4 +-
> >  tools/testing/vma/main.c                   |  55 ++++
> >  tools/testing/vma/shared.c                 | 131 ++++++++
> >  tools/testing/vma/shared.h                 | 114 +++++++
> >  tools/testing/vma/{vma.c => tests/merge.c} | 332 +--------------------
> >  tools/testing/vma/tests/mmap.c             |  57 ++++
> >  tools/testing/vma/tests/vma.c              |  39 +++
> >  tools/testing/vma/vma_internal.h           |   9 -
> >  8 files changed, 406 insertions(+), 335 deletions(-)
> >  create mode 100644 tools/testing/vma/main.c
> >  create mode 100644 tools/testing/vma/shared.c
> >  create mode 100644 tools/testing/vma/shared.h
> >  rename tools/testing/vma/{vma.c => tests/merge.c} (82%)
> >  create mode 100644 tools/testing/vma/tests/mmap.c
> >  create mode 100644 tools/testing/vma/tests/vma.c
> >
> > diff --git a/tools/testing/vma/Makefile b/tools/testing/vma/Makefile
> > index 66f3831a668f..94133d9d3955 100644
> > --- a/tools/testing/vma/Makefile
> > +++ b/tools/testing/vma/Makefile
> > @@ -6,10 +6,10 @@ default: vma
> >
> >  include ../shared/shared.mk
> >
> > -OFILES = $(SHARED_OFILES) vma.o maple-shim.o
> > +OFILES = $(SHARED_OFILES) main.o shared.o maple-shim.o
> >  TARGETS = vma
> >
> > -vma.o: vma.c vma_internal.h ../../../mm/vma.c ../../../mm/vma_init.c ../../../mm/vma_exec.c ../../../mm/vma.h
> > +main.o: main.c shared.c shared.h vma_internal.h tests/merge.c tests/mmap.c tests/vma.c ../../../mm/vma.c ../../../mm/vma_init.c ../../../mm/vma_exec.c ../../../mm/vma.h
> >
> >  vma:	$(OFILES)
> >  	$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
> > diff --git a/tools/testing/vma/main.c b/tools/testing/vma/main.c
> > new file mode 100644
> > index 000000000000..49b09e97a51f
> > --- /dev/null
> > +++ b/tools/testing/vma/main.c
> > @@ -0,0 +1,55 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#include "shared.h"
> > +/*
> > + * Directly import the VMA implementation here. Our vma_internal.h wrapper
> > + * provides userland-equivalent functionality for everything vma.c uses.
> > + */
> > +#include "../../../mm/vma_init.c"
> > +#include "../../../mm/vma_exec.c"
> > +#include "../../../mm/vma.c"
> > +
> > +/* Tests are included directly so they can test static functions in mm/vma.c. */
> > +#include "tests/merge.c"
> > +#include "tests/mmap.c"
> > +#include "tests/vma.c"
> > +
> > +/* Helper functions which utilise static kernel functions. */
> > +
> > +struct vm_area_struct *merge_existing(struct vma_merge_struct *vmg)
> > +{
> > +	struct vm_area_struct *vma;
> > +
> > +	vma = vma_merge_existing_range(vmg);
> > +	if (vma)
> > +		vma_assert_attached(vma);
> > +	return vma;
> > +}
> > +
> > +int attach_vma(struct mm_struct *mm, struct vm_area_struct *vma)
> > +{
> > +	int res;
> > +
> > +	res = vma_link(mm, vma);
> > +	if (!res)
> > +		vma_assert_attached(vma);
> > +	return res;
> > +}
> > +
> > +/* Main test running which invokes tests/ *.c runners. */
> > +int main(void)
> > +{
> > +	int num_tests = 0, num_fail = 0;
> > +
> > +	maple_tree_init();
> > +	vma_state_init();
> > +
> > +	run_merge_tests(&num_tests, &num_fail);
> > +	run_mmap_tests(&num_tests, &num_fail);
> > +	run_vma_tests(&num_tests, &num_fail);
> > +
> > +	printf("%d tests run, %d passed, %d failed.\n",
> > +	       num_tests, num_tests - num_fail, num_fail);
> > +
> > +	return num_fail == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> > +}
> > diff --git a/tools/testing/vma/shared.c b/tools/testing/vma/shared.c
> > new file mode 100644
> > index 000000000000..bda578cc3304
> > --- /dev/null
> > +++ b/tools/testing/vma/shared.c
> > @@ -0,0 +1,131 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#include "shared.h"
> > +
> > +
> > +bool fail_prealloc;
> > +unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
> > +unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
> > +unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT;
> > +
> > +const struct vm_operations_struct vma_dummy_vm_ops;
> > +struct anon_vma dummy_anon_vma;
> > +struct task_struct __current;
> > +
> > +struct vm_area_struct *alloc_vma(struct mm_struct *mm,
> > +		unsigned long start, unsigned long end,
> > +		pgoff_t pgoff, vm_flags_t vm_flags)
> > +{
> > +	struct vm_area_struct *vma = vm_area_alloc(mm);
> > +
> > +	if (vma == NULL)
> > +		return NULL;
> > +
> > +	vma->vm_start = start;
> > +	vma->vm_end = end;
> > +	vma->vm_pgoff = pgoff;
> > +	vm_flags_reset(vma, vm_flags);
> > +	vma_assert_detached(vma);
> > +
> > +	return vma;
> > +}
> > +
> > +void detach_free_vma(struct vm_area_struct *vma)
> > +{
> > +	vma_mark_detached(vma);
> > +	vm_area_free(vma);
> > +}
> > +
> > +struct vm_area_struct *alloc_and_link_vma(struct mm_struct *mm,
> > +		unsigned long start, unsigned long end,
> > +		pgoff_t pgoff, vm_flags_t vm_flags)
> > +{
> > +	struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, vm_flags);
> > +
> > +	if (vma == NULL)
> > +		return NULL;
> > +
> > +	if (attach_vma(mm, vma)) {
> > +		detach_free_vma(vma);
> > +		return NULL;
> > +	}
> > +
> > +	/*
> > +	 * Reset this counter which we use to track whether writes have
> > +	 * begun. Linking to the tree will have caused this to be incremented,
> > +	 * which means we will get a false positive otherwise.
> > +	 */
> > +	vma->vm_lock_seq = UINT_MAX;
> > +
> > +	return vma;
> > +}
> > +
> > +void reset_dummy_anon_vma(void)
> > +{
> > +	dummy_anon_vma.was_cloned = false;
> > +	dummy_anon_vma.was_unlinked = false;
> > +}
> > +
> > +int cleanup_mm(struct mm_struct *mm, struct vma_iterator *vmi)
> > +{
> > +	struct vm_area_struct *vma;
> > +	int count = 0;
> > +
> > +	fail_prealloc = false;
> > +	reset_dummy_anon_vma();
> > +
> > +	vma_iter_set(vmi, 0);
> > +	for_each_vma(*vmi, vma) {
> > +		detach_free_vma(vma);
> > +		count++;
> > +	}
> > +
> > +	mtree_destroy(&mm->mm_mt);
> > +	mm->map_count = 0;
> > +	return count;
> > +}
> > +
> > +bool vma_write_started(struct vm_area_struct *vma)
> > +{
> > +	int seq = vma->vm_lock_seq;
> > +
> > +	/* We reset after each check. */
> > +	vma->vm_lock_seq = UINT_MAX;
> > +
> > +	/* The vma_start_write() stub simply increments this value. */
> > +	return seq > -1;
> > +}
> > +
> > +void __vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > +		struct anon_vma_chain *avc, struct anon_vma *anon_vma)
> > +{
> > +	vma->anon_vma = anon_vma;
> > +	INIT_LIST_HEAD(&vma->anon_vma_chain);
> > +	list_add(&avc->same_vma, &vma->anon_vma_chain);
> > +	avc->anon_vma = vma->anon_vma;
> > +}
> > +
> > +void vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > +		struct anon_vma_chain *avc)
> > +{
> > +	__vma_set_dummy_anon_vma(vma, avc, &dummy_anon_vma);
> > +}
> > +
> > +struct task_struct *get_current(void)
> > +{
> > +	return &__current;
> > +}
> > +
> > +unsigned long rlimit(unsigned int limit)
> > +{
> > +	return (unsigned long)-1;
> > +}
> > +
> > +void vma_set_range(struct vm_area_struct *vma,
> > +		   unsigned long start, unsigned long end,
> > +		   pgoff_t pgoff)
> > +{
> > +	vma->vm_start = start;
> > +	vma->vm_end = end;
> > +	vma->vm_pgoff = pgoff;
> > +}
> > diff --git a/tools/testing/vma/shared.h b/tools/testing/vma/shared.h
> > new file mode 100644
> > index 000000000000..6c64211cfa22
> > --- /dev/null
> > +++ b/tools/testing/vma/shared.h
> > @@ -0,0 +1,114 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#pragma once
> > +
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +
> > +#include "generated/bit-length.h"
> > +#include "maple-shared.h"
> > +#include "vma_internal.h"
> > +#include "../../../mm/vma.h"
> > +
> > +/* Simple test runner. Assumes local num_[fail, tests] counters. */
> > +#define TEST(name)							\
> > +	do {								\
> > +		(*num_tests)++;						\
> > +		if (!test_##name()) {					\
> > +			(*num_fail)++;					\
> > +			fprintf(stderr, "Test " #name " FAILED\n");	\
> > +		}							\
> > +	} while (0)
> > +
> > +#define ASSERT_TRUE(_expr)						\
> > +	do {								\
> > +		if (!(_expr)) {						\
> > +			fprintf(stderr,					\
> > +				"Assert FAILED at %s:%d:%s(): %s is FALSE.\n", \
> > +				__FILE__, __LINE__, __FUNCTION__, #_expr); \
> > +			return false;					\
> > +		}							\
> > +	} while (0)
> > +
> > +#define ASSERT_FALSE(_expr) ASSERT_TRUE(!(_expr))
> > +#define ASSERT_EQ(_val1, _val2) ASSERT_TRUE((_val1) == (_val2))
> > +#define ASSERT_NE(_val1, _val2) ASSERT_TRUE((_val1) != (_val2))
> > +
> > +#define IS_SET(_val, _flags) ((_val & _flags) == _flags)
> > +
> > +extern bool fail_prealloc;
> > +
> > +/* Override vma_iter_prealloc() so we can choose to fail it. */
> > +#define vma_iter_prealloc(vmi, vma)					\
> > +	(fail_prealloc ? -ENOMEM : mas_preallocate(&(vmi)->mas, (vma), GFP_KERNEL))
> > +
> > +#define CONFIG_DEFAULT_MMAP_MIN_ADDR 65536
> > +
> > +extern unsigned long mmap_min_addr;
> > +extern unsigned long dac_mmap_min_addr;
> > +extern unsigned long stack_guard_gap;
> > +
> > +extern const struct vm_operations_struct vma_dummy_vm_ops;
> > +extern struct anon_vma dummy_anon_vma;
> > +extern struct task_struct __current;
> > +
> > +/*
> > + * Helper function which provides a wrapper around a merge existing VMA
> > + * operation.
> > + *
> > + * Declared in main.c as uses static VMA function.
> > + */
> > +struct vm_area_struct *merge_existing(struct vma_merge_struct *vmg);
> > +
> > +/*
> > + * Helper function to allocate a VMA and link it to the tree.
> > + *
> > + * Declared in main.c as uses static VMA function.
> > + */
> > +int attach_vma(struct mm_struct *mm, struct vm_area_struct *vma);
> > +
> > +/* Helper function providing a dummy vm_ops->close() method.*/
> > +static inline void dummy_close(struct vm_area_struct *)
> > +{
> > +}
> > +
> > +/* Helper function to simply allocate a VMA. */
> > +struct vm_area_struct *alloc_vma(struct mm_struct *mm,
> > +		unsigned long start, unsigned long end,
> > +		pgoff_t pgoff, vm_flags_t vm_flags);
> > +
> > +/* Helper function to detach and free a VMA. */
> > +void detach_free_vma(struct vm_area_struct *vma);
> > +
> > +/* Helper function to allocate a VMA and link it to the tree. */
> > +struct vm_area_struct *alloc_and_link_vma(struct mm_struct *mm,
> > +		unsigned long start, unsigned long end,
> > +		pgoff_t pgoff, vm_flags_t vm_flags);
> > +
> > +/*
> > + * Helper function to reset the dummy anon_vma to indicate it has not been
> > + * duplicated.
> > + */
> > +void reset_dummy_anon_vma(void);
> > +
> > +/*
> > + * Helper function to remove all VMAs and destroy the maple tree associated with
> > + * a virtual address space. Returns a count of VMAs in the tree.
> > + */
> > +int cleanup_mm(struct mm_struct *mm, struct vma_iterator *vmi);
> > +
> > +/* Helper function to determine if VMA has had vma_start_write() performed. */
> > +bool vma_write_started(struct vm_area_struct *vma);
> > +
> > +void __vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > +		struct anon_vma_chain *avc, struct anon_vma *anon_vma);
> > +
> > +/* Provide a simple dummy VMA/anon_vma dummy setup for testing. */
> > +void vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > +			    struct anon_vma_chain *avc);
> > +
> > +/* Helper function to specify a VMA's range. */
> > +void vma_set_range(struct vm_area_struct *vma,
> > +		   unsigned long start, unsigned long end,
> > +		   pgoff_t pgoff);
> > diff --git a/tools/testing/vma/vma.c b/tools/testing/vma/tests/merge.c
> > similarity index 82%
> > rename from tools/testing/vma/vma.c
> > rename to tools/testing/vma/tests/merge.c
> > index 93d21bc7e112..3708dc6945b0 100644
> > --- a/tools/testing/vma/vma.c
> > +++ b/tools/testing/vma/tests/merge.c
> > @@ -1,132 +1,5 @@
> >  // SPDX-License-Identifier: GPL-2.0-or-later
> >
> > -#include <stdbool.h>
> > -#include <stdio.h>
> > -#include <stdlib.h>
> > -
> > -#include "generated/bit-length.h"
> > -
> > -#include "maple-shared.h"
> > -#include "vma_internal.h"
> > -
> > -/* Include so header guard set. */
> > -#include "../../../mm/vma.h"
> > -
> > -static bool fail_prealloc;
> > -
> > -/* Then override vma_iter_prealloc() so we can choose to fail it. */
> > -#define vma_iter_prealloc(vmi, vma)					\
> > -	(fail_prealloc ? -ENOMEM : mas_preallocate(&(vmi)->mas, (vma), GFP_KERNEL))
> > -
> > -#define CONFIG_DEFAULT_MMAP_MIN_ADDR 65536
> > -
> > -unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
> > -unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
> > -unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT;
> > -
> > -/*
> > - * Directly import the VMA implementation here. Our vma_internal.h wrapper
> > - * provides userland-equivalent functionality for everything vma.c uses.
> > - */
> > -#include "../../../mm/vma_init.c"
> > -#include "../../../mm/vma_exec.c"
> > -#include "../../../mm/vma.c"
> > -
> > -const struct vm_operations_struct vma_dummy_vm_ops;
> > -static struct anon_vma dummy_anon_vma;
> > -
> > -#define ASSERT_TRUE(_expr)						\
> > -	do {								\
> > -		if (!(_expr)) {						\
> > -			fprintf(stderr,					\
> > -				"Assert FAILED at %s:%d:%s(): %s is FALSE.\n", \
> > -				__FILE__, __LINE__, __FUNCTION__, #_expr); \
> > -			return false;					\
> > -		}							\
> > -	} while (0)
> > -#define ASSERT_FALSE(_expr) ASSERT_TRUE(!(_expr))
> > -#define ASSERT_EQ(_val1, _val2) ASSERT_TRUE((_val1) == (_val2))
> > -#define ASSERT_NE(_val1, _val2) ASSERT_TRUE((_val1) != (_val2))
> > -
> > -#define IS_SET(_val, _flags) ((_val & _flags) == _flags)
> > -
> > -static struct task_struct __current;
> > -
> > -struct task_struct *get_current(void)
> > -{
> > -	return &__current;
> > -}
> > -
> > -unsigned long rlimit(unsigned int limit)
> > -{
> > -	return (unsigned long)-1;
> > -}
> > -
> > -/* Helper function to simply allocate a VMA. */
> > -static struct vm_area_struct *alloc_vma(struct mm_struct *mm,
> > -					unsigned long start,
> > -					unsigned long end,
> > -					pgoff_t pgoff,
> > -					vm_flags_t vm_flags)
> > -{
> > -	struct vm_area_struct *vma = vm_area_alloc(mm);
> > -
> > -	if (vma == NULL)
> > -		return NULL;
> > -
> > -	vma->vm_start = start;
> > -	vma->vm_end = end;
> > -	vma->vm_pgoff = pgoff;
> > -	vm_flags_reset(vma, vm_flags);
> > -	vma_assert_detached(vma);
> > -
> > -	return vma;
> > -}
> > -
> > -/* Helper function to allocate a VMA and link it to the tree. */
> > -static int attach_vma(struct mm_struct *mm, struct vm_area_struct *vma)
> > -{
> > -	int res;
> > -
> > -	res = vma_link(mm, vma);
> > -	if (!res)
> > -		vma_assert_attached(vma);
> > -	return res;
> > -}
> > -
> > -static void detach_free_vma(struct vm_area_struct *vma)
> > -{
> > -	vma_mark_detached(vma);
> > -	vm_area_free(vma);
> > -}
> > -
> > -/* Helper function to allocate a VMA and link it to the tree. */
> > -static struct vm_area_struct *alloc_and_link_vma(struct mm_struct *mm,
> > -						 unsigned long start,
> > -						 unsigned long end,
> > -						 pgoff_t pgoff,
> > -						 vm_flags_t vm_flags)
> > -{
> > -	struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, vm_flags);
> > -
> > -	if (vma == NULL)
> > -		return NULL;
> > -
> > -	if (attach_vma(mm, vma)) {
> > -		detach_free_vma(vma);
> > -		return NULL;
> > -	}
> > -
> > -	/*
> > -	 * Reset this counter which we use to track whether writes have
> > -	 * begun. Linking to the tree will have caused this to be incremented,
> > -	 * which means we will get a false positive otherwise.
> > -	 */
> > -	vma->vm_lock_seq = UINT_MAX;
> > -
> > -	return vma;
> > -}
> > -
> >  /* Helper function which provides a wrapper around a merge new VMA operation. */
> >  static struct vm_area_struct *merge_new(struct vma_merge_struct *vmg)
> >  {
> > @@ -146,20 +19,6 @@ static struct vm_area_struct *merge_new(struct vma_merge_struct *vmg)
> >  	return vma;
> >  }
> >
> > -/*
> > - * Helper function which provides a wrapper around a merge existing VMA
> > - * operation.
> > - */
> > -static struct vm_area_struct *merge_existing(struct vma_merge_struct *vmg)
> > -{
> > -	struct vm_area_struct *vma;
> > -
> > -	vma = vma_merge_existing_range(vmg);
> > -	if (vma)
> > -		vma_assert_attached(vma);
> > -	return vma;
> > -}
> > -
> >  /*
> >   * Helper function which provides a wrapper around the expansion of an existing
> >   * VMA.
> > @@ -173,8 +32,8 @@ static int expand_existing(struct vma_merge_struct *vmg)
> >   * Helper function to reset merge state the associated VMA iterator to a
> >   * specified new range.
> >   */
> > -static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
> > -			  unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags)
> > +void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
> > +		   unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags)
> >  {
> >  	vma_iter_set(vmg->vmi, start);
> >
> > @@ -197,8 +56,8 @@ static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
> >
> >  /* Helper function to set both the VMG range and its anon_vma. */
> >  static void vmg_set_range_anon_vma(struct vma_merge_struct *vmg, unsigned long start,
> > -				   unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags,
> > -				   struct anon_vma *anon_vma)
> > +		unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags,
> > +		struct anon_vma *anon_vma)
> >  {
> >  	vmg_set_range(vmg, start, end, pgoff, vm_flags);
> >  	vmg->anon_vma = anon_vma;
> > @@ -211,10 +70,9 @@ static void vmg_set_range_anon_vma(struct vma_merge_struct *vmg, unsigned long s
> >   * VMA, link it to the maple tree and return it.
> >   */
> >  static struct vm_area_struct *try_merge_new_vma(struct mm_struct *mm,
> > -						struct vma_merge_struct *vmg,
> > -						unsigned long start, unsigned long end,
> > -						pgoff_t pgoff, vm_flags_t vm_flags,
> > -						bool *was_merged)
> > +		struct vma_merge_struct *vmg, unsigned long start,
> > +		unsigned long end, pgoff_t pgoff, vm_flags_t vm_flags,
> > +		bool *was_merged)
> >  {
> >  	struct vm_area_struct *merged;
> >
> > @@ -234,72 +92,6 @@ static struct vm_area_struct *try_merge_new_vma(struct mm_struct *mm,
> >  	return alloc_and_link_vma(mm, start, end, pgoff, vm_flags);
> >  }
> >
> > -/*
> > - * Helper function to reset the dummy anon_vma to indicate it has not been
> > - * duplicated.
> > - */
> > -static void reset_dummy_anon_vma(void)
> > -{
> > -	dummy_anon_vma.was_cloned = false;
> > -	dummy_anon_vma.was_unlinked = false;
> > -}
> > -
> > -/*
> > - * Helper function to remove all VMAs and destroy the maple tree associated with
> > - * a virtual address space. Returns a count of VMAs in the tree.
> > - */
> > -static int cleanup_mm(struct mm_struct *mm, struct vma_iterator *vmi)
> > -{
> > -	struct vm_area_struct *vma;
> > -	int count = 0;
> > -
> > -	fail_prealloc = false;
> > -	reset_dummy_anon_vma();
> > -
> > -	vma_iter_set(vmi, 0);
> > -	for_each_vma(*vmi, vma) {
> > -		detach_free_vma(vma);
> > -		count++;
> > -	}
> > -
> > -	mtree_destroy(&mm->mm_mt);
> > -	mm->map_count = 0;
> > -	return count;
> > -}
> > -
> > -/* Helper function to determine if VMA has had vma_start_write() performed. */
> > -static bool vma_write_started(struct vm_area_struct *vma)
> > -{
> > -	int seq = vma->vm_lock_seq;
> > -
> > -	/* We reset after each check. */
> > -	vma->vm_lock_seq = UINT_MAX;
> > -
> > -	/* The vma_start_write() stub simply increments this value. */
> > -	return seq > -1;
> > -}
> > -
> > -/* Helper function providing a dummy vm_ops->close() method.*/
> > -static void dummy_close(struct vm_area_struct *)
> > -{
> > -}
> > -
> > -static void __vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > -				     struct anon_vma_chain *avc,
> > -				     struct anon_vma *anon_vma)
> > -{
> > -	vma->anon_vma = anon_vma;
> > -	INIT_LIST_HEAD(&vma->anon_vma_chain);
> > -	list_add(&avc->same_vma, &vma->anon_vma_chain);
> > -	avc->anon_vma = vma->anon_vma;
> > -}
> > -
> > -static void vma_set_dummy_anon_vma(struct vm_area_struct *vma,
> > -				   struct anon_vma_chain *avc)
> > -{
> > -	__vma_set_dummy_anon_vma(vma, avc, &dummy_anon_vma);
> > -}
> > -
> >  static bool test_simple_merge(void)
> >  {
> >  	struct vm_area_struct *vma;
> > @@ -1616,39 +1408,6 @@ static bool test_merge_extend(void)
> >  	return true;
> >  }
> >
> > -static bool test_copy_vma(void)
> > -{
> > -	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> > -	struct mm_struct mm = {};
> > -	bool need_locks = false;
> > -	VMA_ITERATOR(vmi, &mm, 0);
> > -	struct vm_area_struct *vma, *vma_new, *vma_next;
> > -
> > -	/* Move backwards and do not merge. */
> > -
> > -	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags);
> > -	vma_new = copy_vma(&vma, 0, 0x2000, 0, &need_locks);
> > -	ASSERT_NE(vma_new, vma);
> > -	ASSERT_EQ(vma_new->vm_start, 0);
> > -	ASSERT_EQ(vma_new->vm_end, 0x2000);
> > -	ASSERT_EQ(vma_new->vm_pgoff, 0);
> > -	vma_assert_attached(vma_new);
> > -
> > -	cleanup_mm(&mm, &vmi);
> > -
> > -	/* Move a VMA into position next to another and merge the two. */
> > -
> > -	vma = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags);
> > -	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x8000, 6, vm_flags);
> > -	vma_new = copy_vma(&vma, 0x4000, 0x2000, 4, &need_locks);
> > -	vma_assert_attached(vma_new);
> > -
> > -	ASSERT_EQ(vma_new, vma_next);
> > -
> > -	cleanup_mm(&mm, &vmi);
> > -	return true;
> > -}
> > -
> >  static bool test_expand_only_mode(void)
> >  {
> >  	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> > @@ -1689,73 +1448,8 @@ static bool test_expand_only_mode(void)
> >  	return true;
> >  }
> >
> > -static bool test_mmap_region_basic(void)
> > -{
> > -	struct mm_struct mm = {};
> > -	unsigned long addr;
> > -	struct vm_area_struct *vma;
> > -	VMA_ITERATOR(vmi, &mm, 0);
> > -
> > -	current->mm = &mm;
> > -
> > -	/* Map at 0x300000, length 0x3000. */
> > -	addr = __mmap_region(NULL, 0x300000, 0x3000,
> > -			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > -			     0x300, NULL);
> > -	ASSERT_EQ(addr, 0x300000);
> > -
> > -	/* Map at 0x250000, length 0x3000. */
> > -	addr = __mmap_region(NULL, 0x250000, 0x3000,
> > -			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > -			     0x250, NULL);
> > -	ASSERT_EQ(addr, 0x250000);
> > -
> > -	/* Map at 0x303000, merging to 0x300000 of length 0x6000. */
> > -	addr = __mmap_region(NULL, 0x303000, 0x3000,
> > -			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > -			     0x303, NULL);
> > -	ASSERT_EQ(addr, 0x303000);
> > -
> > -	/* Map at 0x24d000, merging to 0x250000 of length 0x6000. */
> > -	addr = __mmap_region(NULL, 0x24d000, 0x3000,
> > -			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > -			     0x24d, NULL);
> > -	ASSERT_EQ(addr, 0x24d000);
> > -
> > -	ASSERT_EQ(mm.map_count, 2);
> > -
> > -	for_each_vma(vmi, vma) {
> > -		if (vma->vm_start == 0x300000) {
> > -			ASSERT_EQ(vma->vm_end, 0x306000);
> > -			ASSERT_EQ(vma->vm_pgoff, 0x300);
> > -		} else if (vma->vm_start == 0x24d000) {
> > -			ASSERT_EQ(vma->vm_end, 0x253000);
> > -			ASSERT_EQ(vma->vm_pgoff, 0x24d);
> > -		} else {
> > -			ASSERT_FALSE(true);
> > -		}
> > -	}
> > -
> > -	cleanup_mm(&mm, &vmi);
> > -	return true;
> > -}
> > -
> > -int main(void)
> > +static void run_merge_tests(int *num_tests, int *num_fail)
> >  {
> > -	int num_tests = 0, num_fail = 0;
> > -
> > -	maple_tree_init();
> > -	vma_state_init();
> > -
> > -#define TEST(name)							\
> > -	do {								\
> > -		num_tests++;						\
> > -		if (!test_##name()) {					\
> > -			num_fail++;					\
> > -			fprintf(stderr, "Test " #name " FAILED\n");	\
> > -		}							\
> > -	} while (0)
> > -
> >  	/* Very simple tests to kick the tyres. */
> >  	TEST(simple_merge);
> >  	TEST(simple_modify);
> > @@ -1771,15 +1465,5 @@ int main(void)
> >  	TEST(dup_anon_vma);
> >  	TEST(vmi_prealloc_fail);
> >  	TEST(merge_extend);
> > -	TEST(copy_vma);
> >  	TEST(expand_only_mode);
> > -
> > -	TEST(mmap_region_basic);
> > -
> > -#undef TEST
> > -
> > -	printf("%d tests run, %d passed, %d failed.\n",
> > -	       num_tests, num_tests - num_fail, num_fail);
> > -
> > -	return num_fail == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
> >  }
> > diff --git a/tools/testing/vma/tests/mmap.c b/tools/testing/vma/tests/mmap.c
> > new file mode 100644
> > index 000000000000..bded4ecbe5db
> > --- /dev/null
> > +++ b/tools/testing/vma/tests/mmap.c
> > @@ -0,0 +1,57 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +static bool test_mmap_region_basic(void)
> > +{
> > +	struct mm_struct mm = {};
> > +	unsigned long addr;
> > +	struct vm_area_struct *vma;
> > +	VMA_ITERATOR(vmi, &mm, 0);
> > +
> > +	current->mm = &mm;
> > +
> > +	/* Map at 0x300000, length 0x3000. */
> > +	addr = __mmap_region(NULL, 0x300000, 0x3000,
> > +			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > +			     0x300, NULL);
> > +	ASSERT_EQ(addr, 0x300000);
> > +
> > +	/* Map at 0x250000, length 0x3000. */
> > +	addr = __mmap_region(NULL, 0x250000, 0x3000,
> > +			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > +			     0x250, NULL);
> > +	ASSERT_EQ(addr, 0x250000);
> > +
> > +	/* Map at 0x303000, merging to 0x300000 of length 0x6000. */
> > +	addr = __mmap_region(NULL, 0x303000, 0x3000,
> > +			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > +			     0x303, NULL);
> > +	ASSERT_EQ(addr, 0x303000);
> > +
> > +	/* Map at 0x24d000, merging to 0x250000 of length 0x6000. */
> > +	addr = __mmap_region(NULL, 0x24d000, 0x3000,
> > +			     VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE,
> > +			     0x24d, NULL);
> > +	ASSERT_EQ(addr, 0x24d000);
> > +
> > +	ASSERT_EQ(mm.map_count, 2);
> > +
> > +	for_each_vma(vmi, vma) {
> > +		if (vma->vm_start == 0x300000) {
> > +			ASSERT_EQ(vma->vm_end, 0x306000);
> > +			ASSERT_EQ(vma->vm_pgoff, 0x300);
> > +		} else if (vma->vm_start == 0x24d000) {
> > +			ASSERT_EQ(vma->vm_end, 0x253000);
> > +			ASSERT_EQ(vma->vm_pgoff, 0x24d);
> > +		} else {
> > +			ASSERT_FALSE(true);
> > +		}
> > +	}
> > +
> > +	cleanup_mm(&mm, &vmi);
> > +	return true;
> > +}
> > +
> > +static void run_mmap_tests(int *num_tests, int *num_fail)
> > +{
> > +	TEST(mmap_region_basic);
> > +}
> > diff --git a/tools/testing/vma/tests/vma.c b/tools/testing/vma/tests/vma.c
> > new file mode 100644
> > index 000000000000..6d9775aee243
> > --- /dev/null
> > +++ b/tools/testing/vma/tests/vma.c
> > @@ -0,0 +1,39 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +static bool test_copy_vma(void)
> > +{
> > +	vm_flags_t vm_flags = VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE;
> > +	struct mm_struct mm = {};
> > +	bool need_locks = false;
> > +	VMA_ITERATOR(vmi, &mm, 0);
> > +	struct vm_area_struct *vma, *vma_new, *vma_next;
> > +
> > +	/* Move backwards and do not merge. */
> > +
> > +	vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vm_flags);
> > +	vma_new = copy_vma(&vma, 0, 0x2000, 0, &need_locks);
> > +	ASSERT_NE(vma_new, vma);
> > +	ASSERT_EQ(vma_new->vm_start, 0);
> > +	ASSERT_EQ(vma_new->vm_end, 0x2000);
> > +	ASSERT_EQ(vma_new->vm_pgoff, 0);
> > +	vma_assert_attached(vma_new);
> > +
> > +	cleanup_mm(&mm, &vmi);
> > +
> > +	/* Move a VMA into position next to another and merge the two. */
> > +
> > +	vma = alloc_and_link_vma(&mm, 0, 0x2000, 0, vm_flags);
> > +	vma_next = alloc_and_link_vma(&mm, 0x6000, 0x8000, 6, vm_flags);
> > +	vma_new = copy_vma(&vma, 0x4000, 0x2000, 4, &need_locks);
> > +	vma_assert_attached(vma_new);
> > +
> > +	ASSERT_EQ(vma_new, vma_next);
> > +
> > +	cleanup_mm(&mm, &vmi);
> > +	return true;
> > +}
> > +
> > +static void run_vma_tests(int *num_tests, int *num_fail)
> > +{
> > +	TEST(copy_vma);
> > +}
> > diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h
> > index 2743f12ecf32..b48ebae3927d 100644
> > --- a/tools/testing/vma/vma_internal.h
> > +++ b/tools/testing/vma/vma_internal.h
> > @@ -1127,15 +1127,6 @@ static inline void mapping_allow_writable(struct address_space *mapping)
> >  	atomic_inc(&mapping->i_mmap_writable);
> >  }
> >
> > -static inline void vma_set_range(struct vm_area_struct *vma,
> > -				 unsigned long start, unsigned long end,
> > -				 pgoff_t pgoff)
> > -{
> > -	vma->vm_start = start;
> > -	vma->vm_end = end;
> > -	vma->vm_pgoff = pgoff;
> > -}
> > -
> >  static inline
> >  struct vm_area_struct *vma_find(struct vma_iterator *vmi, unsigned long max)
> >  {
> > --
> > 2.52.0
> >

^ permalink raw reply

* Re: [PATCH] man/man7/kernel_lockdown.7: remove Secure Boot untruth
From: Alejandro Colomar @ 2026-02-10 22:03 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Alyssa Ross, David Howells, Nicolas Bouchinet, Xiu Jianfeng,
	linux-security-module, linux-man
In-Reply-To: <252F93CE-E2C6-4D5A-9B2B-1B4907EEE0BA@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 1684 bytes --]

Hi Heinrich,

On 2026-02-03T21:47:57+0100, Heinrich Schuchardt wrote:
> Am 3. Februar 2026 21:27:44 MEZ schrieb Alejandro Colomar <alx@kernel.org>:
> >Hi Alyssa,
> >
> >On 2026-02-03T20:53:33+0100, Alyssa Ross wrote:
> >> On Tue, Feb 03, 2026 at 08:50:01PM +0100, Alyssa Ross wrote:
> >> > This is true for Fedora, where this page was sourced from, but I don't
> >> > believe it has ever been true for the mainline kernel, because Linus
> >> > rejected it.
> >> >
> >> > Link: https://bbs.archlinux.org/viewtopic.php?pid=2088704#p2088704
> >> > Link: https://lore.kernel.org/lkml/CA+55aFzYbpRAdma0PvqE+9ygySuKzNKByqOzzMufBoovXVnfPw@mail.gmail.com/
> >> > Fixes: bb509e6fc ("kernel_lockdown.7: New page documenting the Kernel Lockdown feature")
> >
> >I've now CCed you in an email documenting the format we use for these.
> >It should be:
> >
> >Fixes: bb509e6fcbae (2020-10-16; "kernel_lockdown.7: New page documenting the Kernel Lockdown feature")
> >
> >I'll amend that myself.
> >
> >> > Signed-off-by: Alyssa Ross <hi@alyssa.is>
> >> 
> >> Just noticed there's a long-open bug for this as well, so additionally:
> >> 
> >> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=213577
> >
> >Thanks!  I'll keep the patch for a few days, in case anyone wants to
> >comment.
> >
> >
> >Have a lovely night!
> >Alex
> >
> 
> Can we move the information from the Notes section to replace the
> removed statement? What causes lockdown is central for users.

We can do that, yes.  Although, I prefer doing it in a separate patch.


Have a lovely night!
Alex

> 
> Best regards
> 
> Heinrich
> 

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH] man/man7/kernel_lockdown.7: remove Secure Boot untruth
From: Alejandro Colomar @ 2026-02-10 22:06 UTC (permalink / raw)
  To: Nicolas Bouchinet
  Cc: Xiu Jianfeng, Alyssa Ross, Heinrich Schuchardt, David Howells,
	linux-security-module, linux-man
In-Reply-To: <aYYP23WUyydsMGyx@archlinux>

[-- Attachment #1: Type: text/plain, Size: 2827 bytes --]

Hi all,

On 2026-02-06T17:08:35+0100, Nicolas Bouchinet wrote:
> On Thu, Feb 05, 2026 at 07:48:02PM +0800, Xiu Jianfeng wrote:
> > On 2/4/2026 3:50 AM, Alyssa Ross wrote:
> > > This is true for Fedora, where this page was sourced from, but I don't
> > > believe it has ever been true for the mainline kernel, because Linus
> > > rejected it.
> > 
> > Yeah, I also found this issue not long ago, but I haven't had time to submit
> > a fix patch yet.
> > 
> > > 
> > > Link: https://bbs.archlinux.org/viewtopic.php?pid=2088704#p2088704
> > > Link: https://lore.kernel.org/lkml/CA+55aFzYbpRAdma0PvqE+9ygySuKzNKByqOzzMufBoovXVnfPw@mail.gmail.com/
> > > Fixes: bb509e6fc ("kernel_lockdown.7: New page documenting the Kernel Lockdown feature")
> > > Signed-off-by: Alyssa Ross <hi@alyssa.is>
> > 
> > I am not sure if appropriate to add my ACK here, if needed, feel free to
> > add:
> > 
> > Acked-by: Xiu Jianfeng <xiujianfeng@huawei.com>
> > 
> 
> You can also add mine too :
> 
> Acked-by: Nicolas Bouchinet <nicolas.bouchinet@oss.cyber.gouv.fr>

Thanks!  I've applied the patch.

    Link: <https://bbs.archlinux.org/viewtopic.php?pid=2088704#p2088704>
    Link: <https://lore.kernel.org/lkml/CA+55aFzYbpRAdma0PvqE+9ygySuKzNKByqOzzMufBoovXVnfPw@mail.gmail.com/>
    Fixes: bb509e6fcbae (2020-10-16; "kernel_lockdown.7: New page documenting the Kernel Lockdown feature")
    Signed-off-by: Alyssa Ross <hi@alyssa.is>
    Message-ID: <20260203195001.20131-1-hi@alyssa.is>
    Acked-by: Xiu Jianfeng <xiujianfeng@huawei.com>
    Message-ID: <aa62e24c-537e-4141-9507-37cd0af19dfc@huawei.com>
    Acked-by: Nicolas Bouchinet <nicolas.bouchinet@oss.cyber.gouv.fr>
    Message-ID: <aYYP23WUyydsMGyx@archlinux>
    Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
    Cc: David Howells <dhowells@redhat.com>
    Cc: <linux-security-module@vger.kernel.org>
    Signed-off-by: Alejandro Colomar <alx@kernel.org>


Have a lovely night!
Alex

> 
> Thank you in advance,
> 
> Nicolas
> 
> > > ---
> > >   man/man7/kernel_lockdown.7 | 3 ---
> > >   1 file changed, 3 deletions(-)
> > > 
> > > diff --git a/man/man7/kernel_lockdown.7 b/man/man7/kernel_lockdown.7
> > > index 5090484ea..5986c8f01 100644
> > > --- a/man/man7/kernel_lockdown.7
> > > +++ b/man/man7/kernel_lockdown.7
> > > @@ -23,9 +23,6 @@ Lockdown: X: Y is restricted, see man kernel_lockdown.7
> > >   .in
> > >   .P
> > >   where X indicates the process name and Y indicates what is restricted.
> > > -.P
> > > -On an EFI-enabled x86 or arm64 machine, lockdown will be automatically enabled
> > > -if the system boots in EFI Secure Boot mode.
> > >   .\"
> > >   .SS Coverage
> > >   When lockdown is in effect, a number of features are disabled or have their
> > 

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* Re: [PATCH v4 2/6] landlock: Control pathname UNIX domain socket resolution by path
From: Günther Noack @ 2026-02-10 23:04 UTC (permalink / raw)
  To: Justin Suess
  Cc: Günther Noack, Mickaël Salaün, John Johansen,
	Tingmao Wang, Jann Horn, linux-security-module,
	Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
	konstantin.meskhidze, Demi Marie Obenour, Alyssa Ross,
	Tahera Fahimi
In-Reply-To: <c060c613-146e-49eb-b9d8-da28cbb680f7@gmail.com>

On Mon, Feb 09, 2026 at 08:11:18AM -0500, Justin Suess wrote:
> On 2/9/26 05:21, Günther Noack wrote:
> > On Mon, Feb 09, 2026 at 12:10:12AM +0100, Günther Noack wrote:
> >> +	/* Lookup for the purpose of saving coredumps is OK. */
> >> +	if (flags & SOCK_COREDUMP)
> >> +		return 0;
> if (unlikely(flags & SOCK_COREDUMP))
>     return 0;

Done.


> >> +
> >> +	/* Only stream, dgram and seqpacket sockets are restricted. */
> >> +	if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET)
> >> +		return 0;
> if (unlikely(type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET))
>     return 0;

Not applicable any more, as I dropped the check per Mickaël's review
in the adjacent mail (I do not think it makes a big difference either
way, TBH.)

–Günther

^ permalink raw reply

* Re: [PATCH v4 2/6] landlock: Control pathname UNIX domain socket resolution by path
From: Günther Noack @ 2026-02-10 23:09 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, John Johansen, Tingmao Wang, Justin Suess,
	Jann Horn, linux-security-module, Samasth Norway Ananda,
	Matthieu Buffet, Mikhail Ivanov, konstantin.meskhidze,
	Demi Marie Obenour, Alyssa Ross, Tahera Fahimi
In-Reply-To: <20260209.IWeigh1theik@digikod.net>

On Mon, Feb 09, 2026 at 06:28:24PM +0100, Mickaël Salaün wrote:
> On Mon, Feb 09, 2026 at 11:21:57AM +0100, Günther Noack wrote:
> > On Mon, Feb 09, 2026 at 12:10:12AM +0100, Günther Noack wrote:
> > > +static int hook_unix_find(const struct path *const path, struct sock *other,
> > > +			  int flags)
> > > +{
> > > +	const struct landlock_ruleset *dom_other;
> > > +	const struct landlock_cred_security *subject;
> > > +	struct layer_access_masks layer_masks;
> > > +	struct landlock_request request = {};
> > > +	static const struct access_masks fs_resolve_unix = {
> > > +		.fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> > > +	};
> > > +	int type = other->sk_type;
> > > +
> > > +	/* Lookup for the purpose of saving coredumps is OK. */
> > > +	if (flags & SOCK_COREDUMP)
> > > +		return 0;
> > > +
> > > +	/* Only stream, dgram and seqpacket sockets are restricted. */
> > > +	if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET)
> > > +		return 0;
> >
> > [...]
>
> You can remove these type checks.  We're building Landlock access
> control wrt to the (moving) current state of Linux, and the goal is to
> cover most/useful access types that currently make sense.  Once access
> type is implemented, it should handle (by default) future features
> related to the kernel object to make sure a sandbox is well covered.
> This LANDLOCK_ACCESS_FS_RESOLVE_UNIX right is really about UNIX sockets
> that can be resolved through the filesystem, so this should handle
> current and potential future UNIX sockets as well.
> 
> If a new named UNIX socket type is created, Landlock should handle that
> with this access right, unless there is a specific semantic (e.g.
> coredump), in which case we'll update the access right, and potentially
> add a new one if it makes sense.
> 
> I was thinking about calling WARN_ON_ONCE() but this is not worth it.

Sounds good, removed the check for the next version.

The possibility of a new UNIX socket type seems anyway pretty
theoretical, and even if an additional type were added, it's not
entirely unthinkable that we would actually want to have it covered
under the same access right.

–Günther

^ permalink raw reply

* Re: [RFC][PATCH v2] ima: Add support for staging measurements for deletion and trimming
From: steven chen @ 2026-02-11  0:09 UTC (permalink / raw)
  To: Roberto Sassu, corbet, zohar, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, nramas, Roberto Sassu, steven chen
In-Reply-To: <52069703-98fc-4667-8c29-446ea73249cb@huaweicloud.com>

On 1/29/2026 12:20 AM, Roberto Sassu wrote:
> On 1/28/2026 10:30 PM, steven chen wrote:
>> On 12/12/2025 9:19 AM, Roberto Sassu wrote:
>>> From: Roberto Sassu <roberto.sassu@huawei.com>
>>>
>>> Introduce the ability of staging the entire (or a portion of the) IMA
>>> measurement list for deletion. Staging means moving the current 
>>> content of
>>> the measurement list to a separate location, and allowing users to 
>>> read and
>>> delete it. This causes the measurement list to be atomically truncated
>>> before new measurements can be added. Staging can be done only once 
>>> at a
>>> time. In the event of kexec(), staging is reverted and staged 
>>> entries will
>>> be carried over to the new kernel.
>>>
>>> User space is responsible to concatenate the staged IMA measurements 
>>> list
>>> portions following the temporal order in which the operations were 
>>> done,
>>> together with the current measurement list. Then, it can send the 
>>> collected
>>> data to the remote verifiers.
>>>
>>> Also introduce the ability of trimming N measurements entries from 
>>> the IMA
>>> measurements list, provided that user space has already read them. 
>>> Trimming
>>> combines staging and deletion in one operation.
>>>
>>> The benefit of these solutions is the ability to free precious kernel
>>> memory, in exchange of delegating user space to reconstruct the full
>>> measurement list from the chunks. No trust needs to be given to user 
>>> space,
>>> since the integrity of the measurement list is protected by the TPM.
>>>
>>> By default, staging/trimming the measurements list does not alter 
>>> the hash
>>> table. When staging/trimming are done, IMA is still able to detect
>>> collisions on the staged and later deleted measurement entries, by 
>>> keeping
>>> the entry digests (only template data are freed).
>>>
>>> However, since during the measurements list serialization only the SHA1
>>> digest is passed, and since there are no template data to 
>>> recalculate the
>>> other digests from, the hash table is currently not populated with 
>>> digests
>>> from staged/deleted entries after kexec().
>>>
>>> Introduce the new kernel option ima_flush_htable to decide whether 
>>> or not
>>> the digests of staged measurement entries are flushed from the hash 
>>> table.
>>>
>>> Then, introduce ascii_runtime_measurements_staged_<algo> and
>>> binary_runtime_measurement_staged_<algo> interfaces to 
>>> stage/trim/delete
>>> the measurements. Use 'echo A > <IMA interface>' and
>>> 'echo D > <IMA interface>' to respectively stage and delete the entire
>>> measurements list. Use 'echo N > <IMA interface>', with N between 1 and
>>> LONG_MAX, to stage the selected portion of the measurements list, and
>>> 'echo -N > <IMA interface>' to trim N measurements entries.
>>>
>>> The ima_measure_users counter (protected by the ima_measure_lock 
>>> mutex) has
>>> been introduced to protect access to the measurements list and the 
>>> staged
>>> part. The open method of all the measurement interfaces has been 
>>> extended
>>> to allow only one writer at a time or, in alternative, multiple 
>>> readers.
>>> The write permission is used to stage/trim/delete the measurements, the
>>> read permission to read them. Write requires also the CAP_SYS_ADMIN
>>> capability.
>>>
>>> Finally, introduce and maintain dedicate counters for the number of
>>> measurement entries and binary size, for the current measurements list
>>> (BINARY_SIZE), for the current measurements list plus staged entries
>>> (BINARY_SIZE_STAGED) useful for kexec() segment allocation, and for the
>>> entire measurement list without staging/trimming (BINARY_SIZE_FULL) 
>>> useful
>>> for the kexec-related critical data records.
>> Is the following possible race condition for staged list:
>>
>> Agent A: create staged list            Staged list A1
>>           new measurement added    Measurement list M1
>>           Two lists in kernel: A1 and M1
>>
>> Agent B: read staged list (A1) to do verification
>>           new measurement added    Measurement list M2
>>           Two lists in kernel: A1 and M2
>>
>> Agent A: verified and remove staged list (A1)
>>           new measurement added    Measurement list M3
>>           One list in kernel: M3
>>
>> Agent C: create staged list            Staged list C1
>>           new measurement added    Measurement list M4
>>           Two lists in kernel: C1 and M4
>>
>> Agent B: remove staged list (?), C1 removed ---this will cause problem
>>           new measurement added    Measurement list M5
>>           One list in kernel: M5
>>
>> Agent C: try to remove staged list(?)
>
> If you remember the patch, we added a read-write protection to the 
> measurements interfaces. As long as you keep the interface open for 
> write no one else can make change on the staging. Sure, you can drop 
> the write, and reopen for read, but then you should expect someone 
> else to operate on the interface.
>
> If you want to be sure no one else changes the staged measurements, 
> just keep the interface open for write, read the staged measurements 
> and delete them.
>
> Roberto
>
For different use cases, we can compare lock time for both staged method 
and trim N method:

t1: user space measurement list lock time
t2: kernel measurement list lock time

     Stage approach use case 1:
               1. read PCR quote
               2. read list
               3. attestation
               4. get N from attestation response
---          5. hold the list in the user space
  ^   ---    6. hold the measurement list
        ^     7. stage the list
t1    t2   8. trim N
        v     9. put the rest of stage back to measurement list
  v   ---    10. release the measurement list
---          11. release the list in the user space
  For this case, agent race condition may happen

   Stage approach use case 2:
               1. read PCR quote
---          2. hold the list in the user space
  ^           3. stage the list
               4. read list
               5. attestation
t1    ---  6. hold the measurement list
          ^   7. get N from attestation response
          t2  8. trim N
          v    9. put the rest of stage back to measurement list
  v    ---   10. release the measurement list
---          11. release the list in the user space
  For this case, no agent race condition happen

the following use case for trim N method

    Trim N approach use case:
              1. read total trimmed T
              2. read PCR quote
              3. read list,
              4. attestation
              5. get N from attestation response
---         6. hold the list in the user space
  ^   ---   7. hold the measurement list
        ^
t1   t2   8. trim with format T:N, update T
        v
  v   ---    9 . release the measurement list
---          10. release the list in the user space
     no agent race condition happen

For all use cases, I think for both t1 and t2, trim N method has better 
result.

Steven

>> Possible solution?
>>    Save the total number trimmed T or tag
>>
>>    Trim request sync this parameter to trim the staged list
>>
>> Regards,
>>
>> Steven
>>
>>> Note: This code derives from the Alt-IMA Huawei project, and is being
>>>        released under the dual license model (GPL-2.0 OR MIT).
>>>
>>> Link: https://github.com/linux-integrity/linux/issues/1
>>> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
>>> --- 


^ permalink raw reply

* [PATCH] selftests/landlock: Remove duplicate include of stdio.h
From: Chen Ni @ 2026-02-11  6:21 UTC (permalink / raw)
  To: mic
  Cc: gnoack, shuah, linux-security-module, linux-kselftest,
	linux-kernel, Chen Ni

Remove duplicate inclusion of stdio.h in fs_bench.c to clean up
redundant code.

Signed-off-by: Chen Ni <nichen@iscas.ac.cn>
---
 tools/testing/selftests/landlock/fs_bench.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tools/testing/selftests/landlock/fs_bench.c b/tools/testing/selftests/landlock/fs_bench.c
index 733c1264e5fd..551cb615ad93 100644
--- a/tools/testing/selftests/landlock/fs_bench.c
+++ b/tools/testing/selftests/landlock/fs_bench.c
@@ -26,7 +26,6 @@
 #include <linux/prctl.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH] selftests/landlock: Remove duplicate include of stdio.h
From: Günther Noack @ 2026-02-11  8:27 UTC (permalink / raw)
  To: Chen Ni
  Cc: mic, gnoack, shuah, linux-security-module, linux-kselftest,
	linux-kernel
In-Reply-To: <20260211062122.2975127-1-nichen@iscas.ac.cn>

On Wed, Feb 11, 2026 at 02:21:22PM +0800, Chen Ni wrote:
> Remove duplicate inclusion of stdio.h in fs_bench.c to clean up
> redundant code.

Thanks, well spotted!

Reviewed-by: Günther Noack <gnoack3000@gmail.com>

^ permalink raw reply

* Re: [PATCH v1] mailmap: Add entry for Mickaël Salaün
From: Paul Moore @ 2026-02-11  9:45 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: linux-kernel, linux-security-module, Günther Noack,
	James Morris
In-Reply-To: <20260207111136.577249-1-mic@digikod.net>

On Sat, Feb 7, 2026 at 6:28 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> My Microsoft address is no longer used.  Add a mailmap entry to reflect
> that.
>
> Cc: Günther Noack <gnoack@google.com>
> Cc: James Morris <jamorris@linux.microsoft.com>
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> ---
>  .mailmap | 1 +
>  1 file changed, 1 insertion(+)

Reviewed-by: Paul Moore <paul@paul-moore.com>

-- 
paul-moore.com

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox