Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
generated/bit-length.h
generated/map-shift.h
generated/autoconf.h
idr.c
radix-tree.c
vma
+21
View File
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-or-later
.PHONY: default clean
default: vma
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)
$(CC) $(CFLAGS) -o $@ $(OFILES) $(LDLIBS)
clean:
$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h generated/bit-length.h generated/autoconf.h
@@ -0,0 +1,97 @@
/* 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 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
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);
}
static inline unsigned long vma_kernel_pagesize(struct vm_area_struct *vma)
{
return PAGE_SIZE;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,435 @@
/* 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 int mmap_action_prepare(struct vm_area_desc *desc)
{
return 0;
}
static inline int mmap_action_complete(struct vm_area_struct *vma,
struct mmap_action *action,
bool is_compat)
{
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 vma_flags_t ksm_vma_flags(struct mm_struct *mm,
const struct file *file, vma_flags_t vma_flags)
{
return vma_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(const 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,
const vma_flags_t *vma_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) {}
static inline bool vma_supports_mlock(const struct vm_area_struct *vma)
{
return false;
}
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _LINUX_MMZONE_H
#define _LINUX_MMZONE_H
#include <linux/atomic.h>
struct pglist_data *first_online_pgdat(void);
struct pglist_data *next_online_pgdat(struct pglist_data *pgdat);
#define for_each_online_pgdat(pgdat) \
for (pgdat = first_online_pgdat(); \
pgdat; \
pgdat = next_online_pgdat(pgdat))
enum zone_type {
__MAX_NR_ZONES
};
#define MAX_NR_ZONES __MAX_NR_ZONES
#define MAX_PAGE_ORDER 10
#define MAX_ORDER_NR_PAGES (1 << MAX_PAGE_ORDER)
#define pageblock_order MAX_PAGE_ORDER
#define pageblock_nr_pages BIT(pageblock_order)
#define pageblock_align(pfn) ALIGN((pfn), pageblock_nr_pages)
#define pageblock_start_pfn(pfn) ALIGN_DOWN((pfn), pageblock_nr_pages)
struct zone {
atomic_long_t managed_pages;
};
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
} pg_data_t;
#endif /* _LINUX_MMZONE_H */
+57
View File
@@ -0,0 +1,57 @@
// 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"
int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
/* 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;
}
+131
View File
@@ -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, vma_flags_t vma_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;
vma->flags = vma_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, vma_flags_t vma_flags)
{
struct vm_area_struct *vma = alloc_vma(mm, start, end, pgoff, vma_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;
}
+132
View File
@@ -0,0 +1,132 @@
// 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 ASSERT_FLAGS_SAME_MASK(_flags, _flags_other) \
ASSERT_TRUE(vma_flags_same_mask((_flags), (_flags_other)))
#define ASSERT_FLAGS_NOT_SAME_MASK(_flags, _flags_other) \
ASSERT_FALSE(vma_flags_same_mask((_flags), (_flags_other)))
#define ASSERT_FLAGS_SAME(_flags, ...) \
ASSERT_TRUE(vma_flags_same(_flags, __VA_ARGS__))
#define ASSERT_FLAGS_NOT_SAME(_flags, ...) \
ASSERT_FALSE(vma_flags_same(_flags, __VA_ARGS__))
#define ASSERT_FLAGS_EMPTY(_flags) \
ASSERT_TRUE(vma_flags_empty(_flags))
#define ASSERT_FLAGS_NONEMPTY(_flags) \
ASSERT_FALSE(vma_flags_empty(_flags))
#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, vma_flags_t vma_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, vma_flags_t vma_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);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-or-later
static bool test_mmap_region_basic(void)
{
const vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_MAYREAD_BIT, VMA_MAYWRITE_BIT);
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, vma_flags, 0x300, NULL);
ASSERT_EQ(addr, 0x300000);
/* Map at 0x250000, length 0x3000. */
addr = __mmap_region(NULL, 0x250000, 0x3000, vma_flags, 0x250, NULL);
ASSERT_EQ(addr, 0x250000);
/* Map at 0x303000, merging to 0x300000 of length 0x6000. */
addr = __mmap_region(NULL, 0x303000, 0x3000, vma_flags, 0x303, NULL);
ASSERT_EQ(addr, 0x303000);
/* Map at 0x24d000, merging to 0x250000 of length 0x6000. */
addr = __mmap_region(NULL, 0x24d000, 0x3000, vma_flags, 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);
}
+656
View File
@@ -0,0 +1,656 @@
// 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];
vma_flags_t converted_flags;
#if NUM_VMA_FLAG_BITS > BITS_PER_LONG
int i;
/* All bits in higher flag values should be zero. */
for (i = 1; i < NUM_VMA_FLAG_BITS / BITS_PER_LONG; i++) {
if (flags.__vma_flags[i] != 0)
return false;
}
#endif
static_assert(sizeof(legacy_flags) == sizeof(unsigned long));
/* Assert that legacy flag helpers work correctly. */
converted_flags = legacy_to_vma_flags(legacy_flags);
ASSERT_FLAGS_SAME_MASK(&converted_flags, flags);
ASSERT_EQ(vma_flags_to_legacy(flags), legacy_flags);
return legacy_val == flags_lower;
}
static bool test_copy_vma(void)
{
vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_MAYREAD_BIT, VMA_MAYWRITE_BIT);
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, vma_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, vma_flags);
vma_next = alloc_and_link_vma(&mm, 0x6000, 0x8000, 6, vma_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_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;
}
#if NUM_VMA_FLAG_BITS > 64
/*
* 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;
}
#endif /* NUM_VMA_FLAG_BITS > 64 */
/* Ensure that vma_flags_test() and friends works correctly. */
static bool test_vma_flags_test(void)
{
vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
struct vm_area_desc desc = {
.vma_flags = flags,
};
struct vm_area_struct vma = {
.flags = flags,
};
#define do_test(_flag) \
ASSERT_TRUE(vma_flags_test(&flags, _flag)); \
ASSERT_TRUE(vma_flags_test_single_mask(&flags, mk_vma_flags(_flag))); \
ASSERT_TRUE(vma_test(&vma, _flag)); \
ASSERT_TRUE(vma_test_single_mask(&vma, mk_vma_flags(_flag))); \
ASSERT_TRUE(vma_desc_test(&desc, _flag))
#define do_test_false(_flag) \
ASSERT_FALSE(vma_flags_test(&flags, _flag)); \
ASSERT_FALSE(vma_flags_test_single_mask(&flags, mk_vma_flags(_flag))); \
ASSERT_FALSE(vma_test(&vma, _flag)); \
ASSERT_FALSE(vma_test_single_mask(&vma, mk_vma_flags(_flag))); \
ASSERT_FALSE(vma_desc_test(&desc, _flag))
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
do_test_false(VMA_MAYWRITE_BIT);
#if NUM_VMA_FLAG_BITS > 64
do_test_false(66);
#endif
#undef do_test
#undef do_test_false
/* We define the _single_mask() variants to return false if empty. */
ASSERT_FALSE(vma_flags_test_single_mask(&flags, EMPTY_VMA_FLAGS));
ASSERT_FALSE(vma_test_single_mask(&vma, EMPTY_VMA_FLAGS));
/* Even when both flags and tested flag mask are empty! */
flags = EMPTY_VMA_FLAGS;
vma.flags = EMPTY_VMA_FLAGS;
ASSERT_FALSE(vma_flags_test_single_mask(&flags, EMPTY_VMA_FLAGS));
ASSERT_FALSE(vma_test_single_mask(&vma, EMPTY_VMA_FLAGS));
return true;
}
/* Ensure that vma_flags_test_any() and friends works correctly. */
static bool test_vma_flags_test_any(void)
{
const vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
struct vm_area_struct vma = {
.flags = flags,
};
struct vm_area_desc desc = {
.vma_flags = flags,
};
#define do_test(...) \
ASSERT_TRUE(vma_flags_test_any(&flags, __VA_ARGS__)); \
ASSERT_TRUE(vma_desc_test_any(&desc, __VA_ARGS__)); \
ASSERT_TRUE(vma_test_any(&vma, __VA_ARGS__));
#define do_test_all_true(...) \
ASSERT_TRUE(vma_flags_test_all(&flags, __VA_ARGS__)); \
ASSERT_TRUE(vma_test_all(&vma, __VA_ARGS__))
#define do_test_all_false(...) \
ASSERT_FALSE(vma_flags_test_all(&flags, __VA_ARGS__)); \
ASSERT_FALSE(vma_test_all(&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);
#if NUM_VMA_FLAG_BITS > 64
/* 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);
#endif
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_any_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
/* Testing all flags against none trivially succeeds. */
ASSERT_TRUE(vma_flags_test_all_mask(&flags, EMPTY_VMA_FLAGS));
ASSERT_TRUE(vma_test_all_mask(&vma, EMPTY_VMA_FLAGS));
#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
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
vma_flags_t mask = mk_vma_flags(VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64
#endif
);
struct vm_area_struct vma = {
.flags = flags,
};
struct vm_area_desc desc = {
.vma_flags = flags,
};
/* Cursory check of _mask() variant, as the helper macros imply. */
vma_flags_clear_mask(&flags, mask);
vma_clear_flags_mask(&vma, mask);
vma_desc_clear_flags_mask(&desc, mask);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FALSE(vma_flags_test_any(&flags, VMA_EXEC_BIT, 64));
ASSERT_FALSE(vma_test_any(&vma, VMA_EXEC_BIT, 64));
ASSERT_FALSE(vma_desc_test_any(&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);
#endif
/*
* 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_clear_flags(&vma, __VA_ARGS__); \
vma_desc_clear_flags(&desc, __VA_ARGS__); \
ASSERT_FALSE(vma_flags_test_any(&flags, __VA_ARGS__)); \
ASSERT_FALSE(vma_test_any(&vma, __VA_ARGS__)); \
ASSERT_FALSE(vma_desc_test_any(&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);
#if NUM_VMA_FLAG_BITS > 64
do_test_and_reset(64);
do_test_and_reset(65);
#endif
/* 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);
#if NUM_VMA_FLAG_BITS > 64
do_test_and_reset(VMA_READ_BIT, 64);
do_test_and_reset(VMA_READ_BIT, 65);
#endif
do_test_and_reset(VMA_WRITE_BIT, VMA_READ_BIT);
do_test_and_reset(VMA_WRITE_BIT, VMA_EXEC_BIT);
#if NUM_VMA_FLAG_BITS > 64
do_test_and_reset(VMA_WRITE_BIT, 64);
do_test_and_reset(VMA_WRITE_BIT, 65);
#endif
do_test_and_reset(VMA_EXEC_BIT, VMA_READ_BIT);
do_test_and_reset(VMA_EXEC_BIT, VMA_WRITE_BIT);
#if NUM_VMA_FLAG_BITS > 64
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);
#endif
/* Three flags. */
#undef do_test_some_missing
#undef do_test_and_reset
return true;
}
/* Ensure that vma_flags_empty() works correctly. */
static bool test_vma_flags_empty(void)
{
vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
ASSERT_FLAGS_NONEMPTY(&flags);
vma_flags_clear(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FLAGS_NONEMPTY(&flags);
vma_flags_clear(&flags, 64, 65);
ASSERT_FLAGS_EMPTY(&flags);
#else
ASSERT_FLAGS_EMPTY(&flags);
#endif
return true;
}
/* Ensure that vma_flags_diff_pair() works correctly. */
static bool test_vma_flags_diff(void)
{
vma_flags_t flags1 = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
vma_flags_t flags2 = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT, VMA_MAYWRITE_BIT,
VMA_MAYEXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65, 66, 67
#endif
);
vma_flags_t diff = vma_flags_diff_pair(&flags1, &flags2);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FLAGS_SAME(&diff, VMA_MAYWRITE_BIT, VMA_MAYEXEC_BIT, 66, 67);
#else
ASSERT_FLAGS_SAME(&diff, VMA_MAYWRITE_BIT, VMA_MAYEXEC_BIT);
#endif
/* Should be the same even if re-ordered. */
diff = vma_flags_diff_pair(&flags2, &flags1);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FLAGS_SAME(&diff, VMA_MAYWRITE_BIT, VMA_MAYEXEC_BIT, 66, 67);
#else
ASSERT_FLAGS_SAME(&diff, VMA_MAYWRITE_BIT, VMA_MAYEXEC_BIT);
#endif
/* Should be no difference when applied against themselves. */
diff = vma_flags_diff_pair(&flags1, &flags1);
ASSERT_FLAGS_EMPTY(&diff);
diff = vma_flags_diff_pair(&flags2, &flags2);
ASSERT_FLAGS_EMPTY(&diff);
/* One set of flags against an empty one should equal the original. */
flags2 = EMPTY_VMA_FLAGS;
diff = vma_flags_diff_pair(&flags1, &flags2);
ASSERT_FLAGS_SAME_MASK(&diff, flags1);
/* A subset should work too. */
flags2 = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT);
diff = vma_flags_diff_pair(&flags1, &flags2);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FLAGS_SAME(&diff, VMA_EXEC_BIT, 64, 65);
#else
ASSERT_FLAGS_SAME(&diff, VMA_EXEC_BIT);
#endif
return true;
}
/* Ensure that vma_flags_and() and friends work correctly. */
static bool test_vma_flags_and(void)
{
vma_flags_t flags1 = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
vma_flags_t flags2 = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT, VMA_MAYWRITE_BIT,
VMA_MAYEXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65, 66, 67
#endif
);
vma_flags_t flags3 = mk_vma_flags(VMA_IO_BIT, VMA_MAYBE_GUARD_BIT
#if NUM_VMA_FLAG_BITS > 64
, 68, 69
#endif
);
vma_flags_t and = vma_flags_and_mask(&flags1, flags2);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
64, 65);
#else
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
#endif
and = vma_flags_and_mask(&flags1, flags1);
ASSERT_FLAGS_SAME_MASK(&and, flags1);
and = vma_flags_and_mask(&flags2, flags2);
ASSERT_FLAGS_SAME_MASK(&and, flags2);
and = vma_flags_and_mask(&flags1, flags3);
ASSERT_FLAGS_EMPTY(&and);
and = vma_flags_and_mask(&flags2, flags3);
ASSERT_FLAGS_EMPTY(&and);
and = vma_flags_and(&flags1, VMA_READ_BIT);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT);
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT);
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
#if NUM_VMA_FLAG_BITS > 64
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
64);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64);
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
64, 65);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT, 64,
65);
#endif
/* And against some missing values. */
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
VMA_IO_BIT);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
VMA_IO_BIT, VMA_RAND_READ_BIT);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
#if NUM_VMA_FLAG_BITS > 64
and = vma_flags_and(&flags1, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT,
VMA_IO_BIT, VMA_RAND_READ_BIT, 69);
ASSERT_FLAGS_SAME(&and, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT);
#endif
return true;
}
/* Ensure append_vma_flags() acts as expected. */
static bool test_append_vma_flags(void)
{
vma_flags_t flags = append_vma_flags(VMA_REMAP_FLAGS, VMA_READ_BIT,
VMA_WRITE_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
ASSERT_FLAGS_SAME(&flags, VMA_IO_BIT, VMA_PFNMAP_BIT,
VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT, VMA_READ_BIT,
VMA_WRITE_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
flags = append_vma_flags(EMPTY_VMA_FLAGS, VMA_READ_BIT, VMA_WRITE_BIT);
ASSERT_FLAGS_SAME(&flags, VMA_READ_BIT, VMA_WRITE_BIT);
return true;
}
/* Assert that vma_flags_count() behaves as expected. */
static bool test_vma_flags_count(void)
{
vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
VMA_EXEC_BIT
#if NUM_VMA_FLAG_BITS > 64
, 64, 65
#endif
);
#if NUM_VMA_FLAG_BITS > 64
ASSERT_EQ(vma_flags_count(&flags), 5);
vma_flags_clear(&flags, 64);
ASSERT_EQ(vma_flags_count(&flags), 4);
vma_flags_clear(&flags, 65);
#endif
ASSERT_EQ(vma_flags_count(&flags), 3);
vma_flags_clear(&flags, VMA_EXEC_BIT);
ASSERT_EQ(vma_flags_count(&flags), 2);
vma_flags_clear(&flags, VMA_WRITE_BIT);
ASSERT_EQ(vma_flags_count(&flags), 1);
vma_flags_clear(&flags, VMA_READ_BIT);
ASSERT_EQ(vma_flags_count(&flags), 0);
return true;
}
static void run_vma_tests(int *num_tests, int *num_fail)
{
TEST(copy_vma);
TEST(vma_flags_unchanged);
TEST(vma_flags_cleared);
#if NUM_VMA_FLAG_BITS > 64
TEST(vma_flags_word);
#endif
TEST(vma_flags_test);
TEST(vma_flags_test_any);
TEST(vma_flags_clear);
TEST(vma_flags_empty);
TEST(vma_flags_diff);
TEST(vma_flags_and);
TEST(append_vma_flags);
TEST(vma_flags_count);
}
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vma_internal.h
*
* Header providing userland wrappers and shims for the functionality provided
* by mm/vma_internal.h.
*
* We make the header guard the same as mm/vma_internal.h, so if this shim
* header is included, it precludes the inclusion of the kernel one.
*/
#ifndef __MM_VMA_INTERNAL_H
#define __MM_VMA_INTERNAL_H
#include <stdlib.h>
#define CONFIG_MMU
#define CONFIG_PER_VMA_LOCK
#ifdef __CONCAT
#undef __CONCAT
#endif
#include <linux/args.h>
#include <linux/atomic.h>
#include <linux/bitmap.h>
#include <linux/list.h>
#include <linux/maple_tree.h>
#include <linux/mm.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/slab.h>
/*
* DUPLICATE typedef definitions from kernel source that have to be declared
* ahead of all other headers.
*/
#define __private
/* NUM_MM_FLAG_BITS defined by test code. */
typedef struct {
__private DECLARE_BITMAP(__mm_flags, NUM_MM_FLAG_BITS);
} mm_flags_t;
/* NUM_VMA_FLAG_BITS defined by test code. */
typedef struct {
DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS);
} __private vma_flags_t;
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;
#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))
#include "include/stubs.h"
#include "include/dup.h"
#include "include/custom.h"
#endif /* __MM_VMA_INTERNAL_H */