summaryrefslogtreecommitdiffstats
path: root/tools/testing/vma/tests
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-09-13 12:27:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-09-13 12:27:00 -0700
commit22098763a10d9c1340827fcf6edab66f153b27f0 (patch)
tree3962f95a34df034652ff93686cdd156c7197f2d9 /tools/testing/vma/tests
downloadlinux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.tar.gz
linux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.zip
Merge tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Don't destroy user event fields when removal fails User event fields are destroyed before the event is removed from visibility. But that can fail leaving the still visible event with no fields. Move the destroying of the fields to after the event is successfully removed from visibility. - Initialize function graph state is fork before calling copy_exec_state() For non-CLONE_VM forks, copy_exec_state() allocates a new task_exec_state. If that allocation fails, ftrace_graph_exit_task() will free the tasks ret_stack pointer. Since that pointer is still using the parent's ret_stack, it mistakenly frees the parent's pointer too. Call ftrace_graph_init() on the task first which will NULL out the new tasks's ret_stack and if the copy fails, it will not free anything. - Remove FGRAPH_MAX_INDEX The macro FGRAPH_MAX_INDEX was added but never used. Remove it. - Save ent_size in function graph printing of nested functions The function graph tracer needs to look at the next event to see if the next event is the return of the current function entry. If it is, it prints a single line: ktime_get(); Otherwise it prints it like a nested function: tick_nohz_irq_exit() { ktime_get(); kcpustat_irq_exit(); } In order to look at the next event, it must save the current event so that it has the information to print from it. It saves the event in the iterator descriptor called "ent". What it doesn't save is the ent_size of the event which is now used to know if the function graph arguments are to be printed. The peek doesn't save the size so the size used happens to be that of the size of the last event that was seen. Save the entry event size in the iterator descriptor so that the correct size is used. - Fix several errors with freeing data in the histogram code The histogram code had a lot of leaked or or incorrect accounting when failures happen. Correct them. - Fix histogram regression of .percent and .graph modifiers Up until 6.3 histogram values could have "percent" or "graph" modifiers that changed how they were printed. But a change that added restricting histograms values from being strings, stack traces and other modifiers inadvertently prevented them from using the percent and graph modifiers, which were legal use cases for values. Put back the percent and graph modifiers. - Fix various typos in the comments - Set the trace_clock before initializing a histogram with clock argument The histogram API allows the user to specific which trace clock to use via a "clock=" string. The histogram is set up first before the clock is checked. If the passed in clock is not valid, it exits without fully fixing up the histogram leaving it on the list and a use-after-free can trigger. Update the clock argument first and if it fails then exit gracefully before the histogram trigger is placed on any lists. - Restore :mod: trailer after parsing in ftrace_set_clr_event The function ftrace_set_clr_event() modifies the parse string and needs to put it back to what was passed in. It searches for ":mod:" via a strsep() but fails to put back the first ':' in the string. Add back the ':' in the passed in string. - Take trace_array reference when opening a tracer options file The options files are dynamically created and some tracers add their own options. When a tracer adds their own list of options, the trace_array holding them has an array to hold the list of options for each tracer. This array increases in size via a krealloc(), and the new entry gets a newly allocated array to hold the options of the new tracer being added. The element in each entry of the tracer's option array holds a pointer back to the trace_array, a pointer to the tracer it is associated to, a pointer to the flags of the option. The issue is that these arrays are freed when the trace_array is freed when its instance it represents is removed from the instances directory. There's a race that an open of one of these options files can happen when the instance is being removed. Add a new helper function to be called by the open function of the options file to iterate all existing trace_arrays under a lock and find the one that has the given option element in one of it's tracer arrays. If found, then update the associated trace_array's reference counter to keep it from being freed. If not found, have the open call return -ENODEV. - Disable interrupts when acquiring the lock in rb_wake_up_waiters() The function rb_wake_up_waiters() assumes it will be called in interrupt context and does not disable irqs when taking cpu_buffer->reader_lock, which can be called in hard interrupt context. The issue is in PREEMPT_RT, this function is called in thread context leaving this lock open to a deadlock. Take the lock with interrupts disabled. - Use rcu_assign_pointer() for tmp_ops filter hash The tmp_ops used in update_ftrace_direct_mod() assigns its filter_hash field directly, but that field is annotated as __rcu and sparse complains. Assign it with rcu_assign_pointer() - Fix use-after-free in enable_trigger_private_data_free() The trace_event_call is accessed through the event_trigger_data's trace_event_file pointer to put the trace_event_call on freeing. The issue is that the trace_event_file data may have been freed already causing a use-after-free. Add a field to the event_trigger_data that points directly to the trace_event_call so that it can decrement its reference directly without needing to go through the trace_event_file. - Fix accounting of buffer data remote headers trace_buffer_desc_size() and trace_remote_alloc_buffer() undercount the number of pages is needed for the asked for size as it doesn't take into account the meta data on each page. Add a helper function to do the calculation properly and use that in these functions. - Catch nr_page_va overflow in ring_buffer_desc sizing The number of pages per remote ring buffer is capped by ring_buffer_desc::nr_page_va (32 bits). A buffer_size large enough to overflow that field would silently allocate a descriptor smaller than what was asked for. - Do not resize the subbuf order if any per_cpu buffer is disabled The mmapping of ring buffers disables resizing the subbuffers, but it is done per-cpu whereas the subbuf size change is done for all the per_cpu buffers under the buffer->mutex. It could change the size of some while the mapping is happening on others. Have the resize of the subbuf order check all the per_cpu buffers under the lock to see if any of them is disabled before starting and causing an inconsistency between buffers that are being mapped. * tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (25 commits) ring-buffer: Check resize_disabled before publishing the new subbuf order tracing/remotes: Catch nr_page_va overflow in ring_buffer_desc sizing tracing/remotes: Account for ring buffer page header in size calculation tracing: Don't dereference trace_event_file in deferred trigger free ftrace: Use rcu_assign_pointer() for tmp_ops filter hash ring-buffer: Acquire the lock with irqsave in rb_wake_up_waiters() tracing: Take trace_array reference when opening a tracer options file tracing: Fix ring_buffer_read_page_size() kernel-doc tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() tracing: Fix memory corruption from a "STACKTRACE" histogram key tracing: Fix memory corruption from the histogram stacktrace modifier tracing: Undo the registration when enabling the histogram trigger fails tracing: Take the reference before publishing the named histogram trigger tracing: Set the trace clock before registering the histogram trigger tracing: Fix typo "preceeded" in comment tracing: Fix typo "availabe" in comment tracing: Let histogram values keep the percent and graph modifiers tracing: Keep the entry count when the histogram stats allocation fails tracing: Free histogram the field rejected for a bad modifier tracing: Free histogram the var ref when its initialization fails ...
Diffstat (limited to 'tools/testing/vma/tests')
-rw-r--r--tools/testing/vma/tests/merge.c1515
-rw-r--r--tools/testing/vma/tests/mmap.c51
-rw-r--r--tools/testing/vma/tests/vma.c700
3 files changed, 2266 insertions, 0 deletions
diff --git a/tools/testing/vma/tests/merge.c b/tools/testing/vma/tests/merge.c
new file mode 100644
index 000000000..acaab2829
--- /dev/null
+++ b/tools/testing/vma/tests/merge.c
@@ -0,0 +1,1515 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* Helper function which provides a wrapper around a merge new VMA operation. */
+static struct vm_area_struct *merge_new(struct vma_merge_struct *vmg)
+{
+ struct vm_area_struct *vma;
+ /*
+ * For convenience, get prev and next VMAs. Which the new VMA operation
+ * requires.
+ */
+ vmg->next = vma_next(vmg->vmi);
+ vmg->prev = vma_prev(vmg->vmi);
+ vma_iter_next_range(vmg->vmi);
+
+ vma = vma_merge_new_range(vmg);
+ if (vma)
+ vma_assert_attached(vma);
+
+ return vma;
+}
+
+/*
+ * Helper function which provides a wrapper around the expansion of an existing
+ * VMA.
+ */
+static int expand_existing(struct vma_merge_struct *vmg)
+{
+ return vma_expand(vmg);
+}
+
+/*
+ * Helper function to reset merge state the associated VMA iterator to a
+ * specified new range.
+ */
+void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
+ unsigned long end, pgoff_t pgoff, vma_flags_t vma_flags)
+{
+ vma_iter_set(vmg->vmi, start);
+
+ vmg->prev = NULL;
+ vmg->middle = NULL;
+ vmg->next = NULL;
+ vmg->target = NULL;
+
+ vmg->start = start;
+ vmg->end = end;
+ vmg->pgoff = pgoff;
+ vmg->anon_pgoff = start >> PAGE_SHIFT;
+ vmg->vma_flags = vma_flags;
+
+ vmg->just_expand = false;
+ vmg->__remove_middle = false;
+ vmg->__remove_next = false;
+ vmg->__adjust_middle_start = false;
+ vmg->__adjust_next_start = false;
+}
+
+/* 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, vma_flags_t vma_flags,
+ struct anon_vma *anon_vma)
+{
+ vmg_set_range(vmg, start, end, pgoff, vma_flags);
+ vmg->anon_vma = anon_vma;
+}
+
+/*
+ * Helper function to try to merge a new VMA.
+ *
+ * Update vmg and the iterator for it and try to merge, otherwise allocate a new
+ * 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, vma_flags_t vma_flags,
+ bool *was_merged)
+{
+ struct vm_area_struct *merged;
+
+ vmg_set_range(vmg, start, end, pgoff, vma_flags);
+
+ merged = merge_new(vmg);
+ if (merged) {
+ *was_merged = true;
+ ASSERT_EQ(vmg->state, VMA_MERGE_SUCCESS);
+ return merged;
+ }
+
+ *was_merged = false;
+
+ ASSERT_EQ(vmg->state, VMA_MERGE_NOMERGE);
+
+ return alloc_and_link_vma(mm, start, end, pgoff, vma_flags);
+}
+
+static bool test_simple_merge(void)
+{
+ struct vm_area_struct *vma;
+ vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, VMA_MAYREAD_BIT,
+ VMA_MAYWRITE_BIT);
+ struct mm_struct mm = {};
+ struct vm_area_struct *vma_left = alloc_vma(&mm, 0, 0x1000, 0, vma_flags);
+ struct vm_area_struct *vma_right = alloc_vma(&mm, 0x2000, 0x3000, 2, vma_flags);
+ VMA_ITERATOR(vmi, &mm, 0x1000);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ .start = 0x1000,
+ .end = 0x2000,
+ .vma_flags = vma_flags,
+ .pgoff = 1,
+ .anon_pgoff = 1,
+ };
+
+ ASSERT_FALSE(attach_vma(&mm, vma_left));
+ ASSERT_FALSE(attach_vma(&mm, vma_right));
+
+ vma = merge_new(&vmg);
+ ASSERT_NE(vma, NULL);
+
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x3000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_FLAGS_SAME_MASK(&vma->flags, vma_flags);
+
+ detach_free_vma(vma);
+ mtree_destroy(&mm.mm_mt);
+
+ return true;
+}
+
+static bool test_simple_modify(void)
+{
+ struct vm_area_struct *vma;
+ vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, VMA_MAYREAD_BIT,
+ VMA_MAYWRITE_BIT);
+ struct mm_struct mm = {};
+ struct vm_area_struct *init_vma = alloc_vma(&mm, 0, 0x3000, 0, vma_flags);
+ VMA_ITERATOR(vmi, &mm, 0x1000);
+
+ ASSERT_FALSE(attach_vma(&mm, init_vma));
+
+ /*
+ * The flags will not be changed, the vma_modify_flags() function
+ * performs the merge/split only.
+ */
+ vma = vma_modify_flags(&vmi, init_vma, init_vma,
+ 0x1000, 0x2000, &vma_flags);
+ ASSERT_NE(vma, NULL);
+ /* We modify the provided VMA, and on split allocate new VMAs. */
+ ASSERT_EQ(vma, init_vma);
+
+ ASSERT_EQ(vma->vm_start, 0x1000);
+ ASSERT_EQ(vma->vm_end, 0x2000);
+ ASSERT_EQ(vma_start_pgoff(vma), 1);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 1);
+
+ /*
+ * Now walk through the three split VMAs and make sure they are as
+ * expected.
+ */
+
+ vma_iter_set(&vmi, 0);
+ vma = vma_iter_load(&vmi);
+
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x1000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+
+ detach_free_vma(vma);
+ vma_iter_clear(&vmi);
+
+ vma = vma_next(&vmi);
+
+ ASSERT_EQ(vma->vm_start, 0x1000);
+ ASSERT_EQ(vma->vm_end, 0x2000);
+ ASSERT_EQ(vma_start_pgoff(vma), 1);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 1);
+
+ detach_free_vma(vma);
+ vma_iter_clear(&vmi);
+
+ vma = vma_next(&vmi);
+
+ ASSERT_EQ(vma->vm_start, 0x2000);
+ ASSERT_EQ(vma->vm_end, 0x3000);
+ ASSERT_EQ(vma_start_pgoff(vma), 2);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 2);
+
+ detach_free_vma(vma);
+ mtree_destroy(&mm.mm_mt);
+
+ return true;
+}
+
+static bool test_simple_expand(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 = {};
+ struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x1000, 0, vma_flags);
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .vmi = &vmi,
+ .target = vma,
+ .start = 0,
+ .end = 0x3000,
+ .pgoff = 0,
+ };
+
+ ASSERT_FALSE(attach_vma(&mm, vma));
+
+ ASSERT_FALSE(expand_existing(&vmg));
+
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x3000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+
+ detach_free_vma(vma);
+ mtree_destroy(&mm.mm_mt);
+
+ return true;
+}
+
+static bool test_simple_shrink(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 = {};
+ struct vm_area_struct *vma = alloc_vma(&mm, 0, 0x3000, 0, vma_flags);
+ VMA_ITERATOR(vmi, &mm, 0);
+
+ ASSERT_FALSE(attach_vma(&mm, vma));
+
+ ASSERT_FALSE(vma_shrink(&vmi, vma, 0x1000));
+
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x1000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+
+ detach_free_vma(vma);
+ mtree_destroy(&mm.mm_mt);
+
+ return true;
+}
+
+static bool __test_merge_new(bool is_sticky, bool a_is_sticky, bool b_is_sticky, bool c_is_sticky)
+{
+ vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
+ VMA_MAYREAD_BIT, VMA_MAYWRITE_BIT);
+ struct mm_struct mm = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain_a = {
+ .anon_vma = &dummy_anon_vma,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain_b = {
+ .anon_vma = &dummy_anon_vma,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain_c = {
+ .anon_vma = &dummy_anon_vma,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain_d = {
+ .anon_vma = &dummy_anon_vma,
+ };
+ const struct vm_operations_struct vm_ops = {
+ .close = dummy_close,
+ };
+ int count;
+ struct vm_area_struct *vma, *vma_a, *vma_b, *vma_c, *vma_d;
+ bool merged;
+
+ if (is_sticky)
+ vma_flags_set_mask(&vma_flags, VMA_STICKY_FLAGS);
+
+ /*
+ * 0123456789abc
+ * AA B CC
+ */
+ vma_a = alloc_and_link_vma(&mm, 0, 0x2000, 0, vma_flags);
+ ASSERT_NE(vma_a, NULL);
+ if (a_is_sticky)
+ vma_flags_set_mask(&vma_a->flags, VMA_STICKY_FLAGS);
+ /* We give each VMA a single avc so we can test anon_vma duplication. */
+ INIT_LIST_HEAD(&vma_a->anon_vma_chain);
+ list_add(&dummy_anon_vma_chain_a.same_vma, &vma_a->anon_vma_chain);
+
+ vma_b = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vma_flags);
+ ASSERT_NE(vma_b, NULL);
+ if (b_is_sticky)
+ vma_flags_set_mask(&vma_b->flags, VMA_STICKY_FLAGS);
+ INIT_LIST_HEAD(&vma_b->anon_vma_chain);
+ list_add(&dummy_anon_vma_chain_b.same_vma, &vma_b->anon_vma_chain);
+
+ vma_c = alloc_and_link_vma(&mm, 0xb000, 0xc000, 0xb, vma_flags);
+ ASSERT_NE(vma_c, NULL);
+ if (c_is_sticky)
+ vma_flags_set_mask(&vma_c->flags, VMA_STICKY_FLAGS);
+ INIT_LIST_HEAD(&vma_c->anon_vma_chain);
+ list_add(&dummy_anon_vma_chain_c.same_vma, &vma_c->anon_vma_chain);
+
+ /*
+ * NO merge.
+ *
+ * 0123456789abc
+ * AA B ** CC
+ */
+ vma_d = try_merge_new_vma(&mm, &vmg, 0x7000, 0x9000, 7, vma_flags, &merged);
+ ASSERT_NE(vma_d, NULL);
+ INIT_LIST_HEAD(&vma_d->anon_vma_chain);
+ list_add(&dummy_anon_vma_chain_d.same_vma, &vma_d->anon_vma_chain);
+ ASSERT_FALSE(merged);
+ ASSERT_EQ(mm.map_count, 4);
+
+ /*
+ * Merge BOTH sides.
+ *
+ * 0123456789abc
+ * AA*B DD CC
+ */
+ vma_a->vm_ops = &vm_ops; /* This should have no impact. */
+ vma_b->anon_vma = &dummy_anon_vma;
+ vma = try_merge_new_vma(&mm, &vmg, 0x2000, 0x3000, 2, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_a);
+ /* Merge with A, delete B. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x4000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 3);
+ if (is_sticky || a_is_sticky || b_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Merge to PREVIOUS VMA.
+ *
+ * 0123456789abc
+ * AAAA* DD CC
+ */
+ vma = try_merge_new_vma(&mm, &vmg, 0x4000, 0x5000, 4, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_a);
+ /* Extend A. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x5000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 3);
+ if (is_sticky || a_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Merge to NEXT VMA.
+ *
+ * 0123456789abc
+ * AAAAA *DD CC
+ */
+ vma_d->anon_vma = &dummy_anon_vma;
+ vma_d->vm_ops = &vm_ops; /* This should have no impact. */
+ vma = try_merge_new_vma(&mm, &vmg, 0x6000, 0x7000, 6, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_d);
+ /* Prepend. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0x6000);
+ ASSERT_EQ(vma->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma), 6);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 6);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 3);
+ if (is_sticky) /* D uses is_sticky. */
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Merge BOTH sides.
+ *
+ * 0123456789abc
+ * AAAAA*DDD CC
+ */
+ vma_d->vm_ops = NULL; /* This would otherwise degrade the merge. */
+ vma = try_merge_new_vma(&mm, &vmg, 0x5000, 0x6000, 5, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_a);
+ /* Merge with A, delete D. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 2);
+ if (is_sticky || a_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Merge to NEXT VMA.
+ *
+ * 0123456789abc
+ * AAAAAAAAA *CC
+ */
+ vma_c->anon_vma = &dummy_anon_vma;
+ vma = try_merge_new_vma(&mm, &vmg, 0xa000, 0xb000, 0xa, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_c);
+ /* Prepend C. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0xa000);
+ ASSERT_EQ(vma->vm_end, 0xc000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0xa);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0xa);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 2);
+ if (is_sticky || c_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Merge BOTH sides.
+ *
+ * 0123456789abc
+ * AAAAAAAAA*CCC
+ */
+ vma = try_merge_new_vma(&mm, &vmg, 0x9000, 0xa000, 0x9, vma_flags, &merged);
+ ASSERT_EQ(vma, vma_a);
+ /* Extend A and delete C. */
+ ASSERT_TRUE(merged);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0xc000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 1);
+ if (is_sticky || a_is_sticky || c_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma->flags, VMA_STICKY_FLAGS));
+
+ /*
+ * Final state.
+ *
+ * 0123456789abc
+ * AAAAAAAAAAAAA
+ */
+
+ count = 0;
+ vma_iter_set(&vmi, 0);
+ for_each_vma(vmi, vma) {
+ ASSERT_NE(vma, NULL);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0xc000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
+
+ detach_free_vma(vma);
+ count++;
+ }
+
+ /* Should only have one VMA left (though freed) after all is done.*/
+ ASSERT_EQ(count, 1);
+
+ mtree_destroy(&mm.mm_mt);
+ return true;
+}
+
+static bool test_merge_new(void)
+{
+ int i, j, k, l;
+
+ /* Generate every possible permutation of sticky flags. */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 2; j++)
+ for (k = 0; k < 2; k++)
+ for (l = 0; l < 2; l++)
+ ASSERT_TRUE(__test_merge_new(i, j, k, l));
+
+ return true;
+}
+
+static bool test_vma_merge_special_flags(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ vma_flag_t special_flags[] = { VMA_IO_BIT, VMA_DONTEXPAND_BIT,
+ VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT };
+ vma_flags_t all_special_flags = EMPTY_VMA_FLAGS;
+ int i;
+ struct vm_area_struct *vma_left, *vma;
+
+ /* Make sure there aren't new VM_SPECIAL flags. */
+ for (i = 0; i < ARRAY_SIZE(special_flags); i++)
+ vma_flags_set(&all_special_flags, special_flags[i]);
+ ASSERT_FLAGS_SAME_MASK(&all_special_flags, VMA_SPECIAL_FLAGS);
+
+ /*
+ * 01234
+ * AAA
+ */
+ vma_left = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ ASSERT_NE(vma_left, NULL);
+
+ /* 1. Set up new VMA with special flag that would otherwise merge. */
+
+ /*
+ * 01234
+ * AAA*
+ *
+ * This should merge if not for the VM_SPECIAL flag.
+ */
+ vmg_set_range(&vmg, 0x3000, 0x4000, 3, vma_flags);
+ for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
+ vma_flag_t special_flag = special_flags[i];
+ vma_flags_t flags = vma_flags;
+
+ vma_flags_set(&flags, special_flag);
+ vma_left->flags = flags;
+ vmg.vma_flags = flags;
+ vma = merge_new(&vmg);
+ ASSERT_EQ(vma, NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+ }
+
+ /* 2. Modify VMA with special flag that would otherwise merge. */
+
+ /*
+ * 01234
+ * AAAB
+ *
+ * Create a VMA to modify.
+ */
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vma_flags);
+ ASSERT_NE(vma, NULL);
+ vmg.middle = vma;
+
+ for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
+ vma_flag_t special_flag = special_flags[i];
+ vma_flags_t flags = vma_flags;
+
+ vma_flags_set(&flags, special_flag);
+ vma_left->flags = flags;
+ vmg.vma_flags = flags;
+ vma = merge_existing(&vmg);
+ ASSERT_EQ(vma, NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+ }
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static bool test_vma_merge_with_close(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ const struct vm_operations_struct vm_ops = {
+ .close = dummy_close,
+ };
+ struct vm_area_struct *vma_prev, *vma_next, *vma;
+
+ /*
+ * When merging VMAs we are not permitted to remove any VMA that has a
+ * vm_ops->close() hook.
+ *
+ * Considering the two possible adjacent VMAs to which a VMA can be
+ * merged:
+ *
+ * [ prev ][ vma ][ next ]
+ *
+ * In no case will we need to delete prev. If the operation is
+ * mergeable, then prev will be extended with one or both of vma and
+ * next deleted.
+ *
+ * As a result, during initial mergeability checks, only
+ * can_vma_merge_before() (which implies the VMA being merged with is
+ * 'next' as shown above) bothers to check to see whether the next VMA
+ * has a vm_ops->close() callback that will need to be called when
+ * removed.
+ *
+ * If it does, then we cannot merge as the resources that the close()
+ * operation potentially clears down are tied only to the existing VMA
+ * range and we have no way of extending those to the nearly merged one.
+ *
+ * We must consider two scenarios:
+ *
+ * A.
+ *
+ * vm_ops->close: - - !NULL
+ * [ prev ][ vma ][ next ]
+ *
+ * Where prev may or may not be present/mergeable.
+ *
+ * This is picked up by a specific check in can_vma_merge_before().
+ *
+ * B.
+ *
+ * vm_ops->close: - !NULL
+ * [ prev ][ vma ]
+ *
+ * Where prev and vma are present and mergeable.
+ *
+ * This is picked up by a specific check in the modified VMA merge.
+ *
+ * IMPORTANT NOTE: We make the assumption that the following case:
+ *
+ * - !NULL NULL
+ * [ prev ][ vma ][ next ]
+ *
+ * Cannot occur, because vma->vm_ops being the same implies the same
+ * vma->vm_file, and therefore this would mean that next->vm_ops->close
+ * would be set too, and thus scenario A would pick this up.
+ */
+
+ /*
+ * The only case of a new VMA merge that results in a VMA being deleted
+ * is one where both the previous and next VMAs are merged - in this
+ * instance the next VMA is deleted, and the previous VMA is extended.
+ *
+ * If we are unable to do so, we reduce the operation to simply
+ * extending the prev VMA and not merging next.
+ *
+ * 0123456789
+ * PPP**NNNN
+ * ->
+ * 0123456789
+ * PPPPPPNNN
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vma_flags);
+ vma_next->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ ASSERT_EQ(merge_new(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x5000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * When modifying an existing VMA there are further cases where we
+ * delete VMAs.
+ *
+ * <>
+ * 0123456789
+ * PPPVV
+ *
+ * In this instance, if vma has a close hook, the merge simply cannot
+ * proceed.
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ /*
+ * The VMA being modified in a way that would otherwise merge should
+ * also fail.
+ */
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * This case is mirrored if merging with next.
+ *
+ * <>
+ * 0123456789
+ * VVNNNN
+ *
+ * In this instance, if vma has a close hook, the merge simply cannot
+ * proceed.
+ */
+
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vma_flags);
+ vma->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ /*
+ * Initially this is misapprehended as an out of memory report, as the
+ * close() check is handled in the same way as anon_vma duplication
+ * failures, however a subsequent patch resolves this.
+ */
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * Finally, we consider two variants of the case where we modify a VMA
+ * to merge with both the previous and next VMAs.
+ *
+ * The first variant is where vma has a close hook. In this instance, no
+ * merge can proceed.
+ *
+ * <>
+ * 0123456789
+ * PPPVVNNNN
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vma_flags);
+ vma->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 3);
+
+ /*
+ * The second variant is where next has a close hook. In this instance,
+ * we reduce the operation to a merge between prev and vma.
+ *
+ * <>
+ * 0123456789
+ * PPPVVNNNN
+ * ->
+ * 0123456789
+ * PPPPPNNNN
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x9000, 5, vma_flags);
+ vma_next->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x5000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ return true;
+}
+
+static bool test_vma_merge_new_with_close(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ struct vm_area_struct *vma_prev = alloc_and_link_vma(&mm, 0, 0x2000, 0, vma_flags);
+ struct vm_area_struct *vma_next = alloc_and_link_vma(&mm, 0x5000, 0x7000, 5, vma_flags);
+ const struct vm_operations_struct vm_ops = {
+ .close = dummy_close,
+ };
+ struct vm_area_struct *vma;
+
+ /*
+ * We should allow the partial merge of a proposed new VMA if the
+ * surrounding VMAs have vm_ops->close() hooks (but are otherwise
+ * compatible), e.g.:
+ *
+ * New VMA
+ * A v-------v B
+ * |-----| |-----|
+ * close close
+ *
+ * Since the rule is to not DELETE a VMA with a close operation, this
+ * should be permitted, only rather than expanding A and deleting B, we
+ * should simply expand A and leave B intact, e.g.:
+ *
+ * New VMA
+ * A B
+ * |------------||-----|
+ * close close
+ */
+
+ /* Have prev and next have a vm_ops->close() hook. */
+ vma_prev->vm_ops = &vm_ops;
+ vma_next->vm_ops = &vm_ops;
+
+ vmg_set_range(&vmg, 0x2000, 0x5000, 2, vma_flags);
+ vma = merge_new(&vmg);
+ ASSERT_NE(vma, NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x5000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_EQ(vma->vm_ops, &vm_ops);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 2);
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static bool __test_merge_existing(bool prev_is_sticky, bool middle_is_sticky, bool next_is_sticky)
+{
+ vma_flags_t vma_flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT,
+ VMA_MAYREAD_BIT, VMA_MAYWRITE_BIT);
+ vma_flags_t prev_flags = vma_flags;
+ vma_flags_t next_flags = vma_flags;
+ struct mm_struct mm = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vm_area_struct *vma, *vma_prev, *vma_next;
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ const struct vm_operations_struct vm_ops = {
+ .close = dummy_close,
+ };
+ struct anon_vma_chain avc = {};
+
+ if (prev_is_sticky)
+ vma_flags_set_mask(&prev_flags, VMA_STICKY_FLAGS);
+ if (middle_is_sticky)
+ vma_flags_set_mask(&vma_flags, VMA_STICKY_FLAGS);
+ if (next_is_sticky)
+ vma_flags_set_mask(&next_flags, VMA_STICKY_FLAGS);
+
+ /*
+ * Merge right case - partial span.
+ *
+ * <->
+ * 0123456789
+ * VVVVNNN
+ * ->
+ * 0123456789
+ * VNNNNNN
+ */
+ vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vma_flags);
+ vma->vm_ops = &vm_ops; /* This should have no impact. */
+ vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, next_flags);
+ vma_next->vm_ops = &vm_ops; /* This should have no impact. */
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, vma_flags, &dummy_anon_vma);
+ vmg.middle = vma;
+ vmg.prev = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+ ASSERT_EQ(merge_existing(&vmg), vma_next);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_next->vm_start, 0x3000);
+ ASSERT_EQ(vma_next->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma_next), 3);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_next), 3);
+ ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
+ ASSERT_EQ(vma->vm_start, 0x2000);
+ ASSERT_EQ(vma->vm_end, 0x3000);
+ ASSERT_EQ(vma_start_pgoff(vma), 2);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 2);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_TRUE(vma_write_started(vma_next));
+ ASSERT_EQ(mm.map_count, 2);
+ if (middle_is_sticky || next_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma_next->flags, VMA_STICKY_FLAGS));
+
+ /* Clear down and reset. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * Merge right case - full span.
+ *
+ * <-->
+ * 0123456789
+ * VVVVNNN
+ * ->
+ * 0123456789
+ * NNNNNNN
+ */
+ vma = alloc_and_link_vma(&mm, 0x2000, 0x6000, 2, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x6000, 0x9000, 6, next_flags);
+ vma_next->vm_ops = &vm_ops; /* This should have no impact. */
+ vmg_set_range_anon_vma(&vmg, 0x2000, 0x6000, 2, vma_flags, &dummy_anon_vma);
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+ ASSERT_EQ(merge_existing(&vmg), vma_next);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_next->vm_start, 0x2000);
+ ASSERT_EQ(vma_next->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma_next), 2);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_next), 2);
+ ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma_next));
+ ASSERT_EQ(mm.map_count, 1);
+ if (middle_is_sticky || next_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma_next->flags, VMA_STICKY_FLAGS));
+
+ /* Clear down and reset. We should have deleted vma. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
+
+ /*
+ * Merge left case - partial span.
+ *
+ * <->
+ * 0123456789
+ * PPPVVVV
+ * ->
+ * 0123456789
+ * PPPPPPV
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
+ vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vma_flags);
+ vma->vm_ops = &vm_ops; /* This should have no impact. */
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x6000, 3, vma_flags, &dummy_anon_vma);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x6000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_EQ(vma->vm_start, 0x6000);
+ ASSERT_EQ(vma->vm_end, 0x7000);
+ ASSERT_EQ(vma_start_pgoff(vma), 6);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 6);
+ ASSERT_TRUE(vma_write_started(vma_prev));
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 2);
+ if (prev_is_sticky || middle_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma_prev->flags, VMA_STICKY_FLAGS));
+
+ /* Clear down and reset. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * Merge left case - full span.
+ *
+ * <-->
+ * 0123456789
+ * PPPVVVV
+ * ->
+ * 0123456789
+ * PPPPPPP
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
+ vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vma_flags);
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vma_flags, &dummy_anon_vma);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x7000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma_prev));
+ ASSERT_EQ(mm.map_count, 1);
+ if (prev_is_sticky || middle_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma_prev->flags, VMA_STICKY_FLAGS));
+
+ /* Clear down and reset. We should have deleted vma. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
+
+ /*
+ * Merge both case.
+ *
+ * <-->
+ * 0123456789
+ * PPPVVVVNNN
+ * ->
+ * 0123456789
+ * PPPPPPPPPP
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
+ vma_prev->vm_ops = &vm_ops; /* This should have no impact. */
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, next_flags);
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vma_flags, &dummy_anon_vma);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_write_started(vma_prev));
+ ASSERT_EQ(mm.map_count, 1);
+ if (prev_is_sticky || middle_is_sticky || next_is_sticky)
+ ASSERT_TRUE(vma_flags_test_any_mask(&vma_prev->flags, VMA_STICKY_FLAGS));
+
+ /* Clear down and reset. We should have deleted prev and next. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 1);
+
+ /*
+ * Non-merge ranges. the modified VMA merge operation assumes that the
+ * caller always specifies ranges within the input VMA so we need only
+ * examine these cases.
+ *
+ * -
+ * -
+ * -
+ * <->
+ * <>
+ * <>
+ * 0123456789a
+ * PPPVVVVVNNN
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, prev_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x8000, 0xa000, 8, next_flags);
+
+ vmg_set_range(&vmg, 0x4000, 0x5000, 4, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ vmg_set_range(&vmg, 0x5000, 0x6000, 5, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ vmg_set_range(&vmg, 0x6000, 0x7000, 6, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ vmg_set_range(&vmg, 0x4000, 0x7000, 4, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ vmg_set_range(&vmg, 0x4000, 0x6000, 4, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ vmg_set_range(&vmg, 0x5000, 0x6000, 5, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
+
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 3);
+
+ return true;
+}
+
+static bool test_merge_existing(void)
+{
+ int i, j, k;
+
+ /* Generate every possible permutation of sticky flags. */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 2; j++)
+ for (k = 0; k < 2; k++)
+ ASSERT_TRUE(__test_merge_existing(i, j, k));
+
+ return true;
+}
+
+static bool test_anon_vma_non_mergeable(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vm_area_struct *vma, *vma_prev, *vma_next;
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain_1 = {};
+ struct anon_vma_chain dummy_anon_vma_chain_2 = {};
+ struct anon_vma dummy_anon_vma_2;
+
+ /*
+ * In the case of modified VMA merge, merging both left and right VMAs
+ * but where prev and next have incompatible anon_vma objects, we revert
+ * to a merge of prev and VMA:
+ *
+ * <-->
+ * 0123456789
+ * PPPVVVVNNN
+ * ->
+ * 0123456789
+ * PPPPPPPNNN
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x7000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vma_flags);
+
+ /*
+ * Give both prev and next single anon_vma_chain fields, so they will
+ * merge with the NULL vmg->anon_vma.
+ *
+ * However, when prev is compared to next, the merge should fail.
+ */
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vma_flags, NULL);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma_prev, &dummy_anon_vma_chain_1);
+ __vma_set_dummy_anon_vma(vma_next, &dummy_anon_vma_chain_2, &dummy_anon_vma_2);
+
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x7000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+ ASSERT_TRUE(vma_write_started(vma_prev));
+ ASSERT_FALSE(vma_write_started(vma_next));
+
+ /* Clear down and reset. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ /*
+ * Now consider the new VMA case. This is equivalent, only adding a new
+ * VMA in a gap between prev and next.
+ *
+ * <-->
+ * 0123456789
+ * PPP****NNN
+ * ->
+ * 0123456789
+ * PPPPPPPNNN
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x7000, 0x9000, 7, vma_flags);
+
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x7000, 3, vma_flags, NULL);
+ vmg.prev = vma_prev;
+ vma_set_dummy_anon_vma(vma_prev, &dummy_anon_vma_chain_1);
+ __vma_set_dummy_anon_vma(vma_next, &dummy_anon_vma_chain_2, &dummy_anon_vma_2);
+
+ vmg.anon_vma = NULL;
+ ASSERT_EQ(merge_new(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x7000);
+ ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
+ ASSERT_TRUE(vma_write_started(vma_prev));
+ ASSERT_FALSE(vma_write_started(vma_next));
+
+ /* Final cleanup. */
+ ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
+
+ return true;
+}
+
+static bool test_dup_anon_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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ struct anon_vma_chain dummy_anon_vma_chain = {
+ .anon_vma = &dummy_anon_vma,
+ };
+ struct vm_area_struct *vma_prev, *vma_next, *vma;
+
+ reset_dummy_anon_vma();
+
+ /*
+ * Expanding a VMA delete the next one duplicates next's anon_vma and
+ * assigns it to the expanded VMA.
+ *
+ * This covers new VMA merging, as these operations amount to a VMA
+ * expand.
+ */
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next->anon_vma = &dummy_anon_vma;
+
+ vmg_set_range(&vmg, 0, 0x5000, 0, vma_flags);
+ vmg.target = vma_prev;
+ vmg.next = vma_next;
+
+ ASSERT_EQ(expand_existing(&vmg), 0);
+
+ /* Will have been cloned. */
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
+
+ /* Cleanup ready for next run. */
+ cleanup_mm(&mm, &vmi);
+
+ /*
+ * next has anon_vma, we assign to prev.
+ *
+ * |<----->|
+ * |-------*********-------|
+ * prev vma next
+ * extend delete delete
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vma_flags);
+
+ /* Initialise avc so mergeability check passes. */
+ INIT_LIST_HEAD(&vma_next->anon_vma_chain);
+ list_add(&dummy_anon_vma_chain.same_vma, &vma_next->anon_vma_chain);
+
+ vma_next->anon_vma = &dummy_anon_vma;
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x8000);
+
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
+
+ cleanup_mm(&mm, &vmi);
+
+ /*
+ * vma has anon_vma, we assign to prev.
+ *
+ * |<----->|
+ * |-------*********-------|
+ * prev vma next
+ * extend delete delete
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vma_flags);
+ vmg.anon_vma = &dummy_anon_vma;
+ vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain);
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x8000);
+
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
+
+ cleanup_mm(&mm, &vmi);
+
+ /*
+ * vma has anon_vma, we assign to prev.
+ *
+ * |<----->|
+ * |-------*************
+ * prev vma
+ * extend shrink/delete
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x8000, 3, vma_flags);
+
+ vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain);
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+
+ ASSERT_EQ(vma_prev->vm_start, 0);
+ ASSERT_EQ(vma_prev->vm_end, 0x5000);
+
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_prev->anon_vma->was_cloned);
+
+ cleanup_mm(&mm, &vmi);
+
+ /*
+ * vma has anon_vma, we assign to next.
+ *
+ * |<----->|
+ * *************-------|
+ * vma next
+ * shrink/delete extend
+ */
+
+ vma = alloc_and_link_vma(&mm, 0, 0x5000, 0, vma_flags);
+ vma_next = alloc_and_link_vma(&mm, 0x5000, 0x8000, 5, vma_flags);
+
+ vma_set_dummy_anon_vma(vma, &dummy_anon_vma_chain);
+ vmg_set_range(&vmg, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma;
+ vmg.middle = vma;
+
+ ASSERT_EQ(merge_existing(&vmg), vma_next);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+
+ ASSERT_EQ(vma_next->vm_start, 0x3000);
+ ASSERT_EQ(vma_next->vm_end, 0x8000);
+
+ ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(vma_next->anon_vma->was_cloned);
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static bool test_vmi_prealloc_fail(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vma_merge_struct vmg = {
+ .mm = &mm,
+ .vmi = &vmi,
+ };
+ struct anon_vma_chain avc = {};
+ struct vm_area_struct *vma_prev, *vma;
+
+ /*
+ * We are merging vma into prev, with vma possessing an anon_vma, which
+ * will be duplicated. We cause the vmi preallocation to fail and assert
+ * the duplicated anon_vma is unlinked.
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma->anon_vma = &dummy_anon_vma;
+
+ vmg_set_range_anon_vma(&vmg, 0x3000, 0x5000, 3, vma_flags, &dummy_anon_vma);
+ vmg.prev = vma_prev;
+ vmg.middle = vma;
+ vma_set_dummy_anon_vma(vma, &avc);
+
+ fail_prealloc = true;
+
+ /* This will cause the merge to fail. */
+ ASSERT_EQ(merge_existing(&vmg), NULL);
+ ASSERT_EQ(vmg.state, VMA_MERGE_ERROR_NOMEM);
+ /* We will already have assigned the anon_vma. */
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ /* And it was both cloned and unlinked. */
+ ASSERT_TRUE(dummy_anon_vma.was_cloned);
+ ASSERT_TRUE(dummy_anon_vma.was_unlinked);
+
+ cleanup_mm(&mm, &vmi); /* Resets fail_prealloc too. */
+
+ /*
+ * We repeat the same operation for expanding a VMA, which is what new
+ * VMA merging ultimately uses too. This asserts that unlinking is
+ * performed in this case too.
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0, 0x3000, 0, vma_flags);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vma->anon_vma = &dummy_anon_vma;
+
+ vmg_set_range(&vmg, 0, 0x5000, 3, vma_flags);
+ vmg.target = vma_prev;
+ vmg.next = vma;
+
+ fail_prealloc = true;
+ ASSERT_EQ(expand_existing(&vmg), -ENOMEM);
+ ASSERT_EQ(vmg.state, VMA_MERGE_ERROR_NOMEM);
+
+ ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
+ ASSERT_TRUE(dummy_anon_vma.was_cloned);
+ ASSERT_TRUE(dummy_anon_vma.was_unlinked);
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static bool test_merge_extend(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0x1000);
+ struct vm_area_struct *vma;
+
+ vma = alloc_and_link_vma(&mm, 0, 0x1000, 0, vma_flags);
+ alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vma_flags);
+
+ /*
+ * Extend a VMA into the gap between itself and the following VMA.
+ * This should result in a merge.
+ *
+ * <->
+ * * *
+ *
+ */
+
+ ASSERT_EQ(vma_merge_extend(&vmi, vma, 0x2000), vma);
+ ASSERT_EQ(vma->vm_start, 0);
+ ASSERT_EQ(vma->vm_end, 0x4000);
+ ASSERT_EQ(vma_start_pgoff(vma), 0);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(mm.map_count, 1);
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static bool test_expand_only_mode(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 = {};
+ VMA_ITERATOR(vmi, &mm, 0);
+ struct vm_area_struct *vma_prev, *vma;
+ VMG_STATE(vmg, &mm, &vmi, 0x5000, 0x9000, vma_flags, 5, 5);
+
+ /*
+ * Place a VMA prior to the one we're expanding so we assert that we do
+ * not erroneously try to traverse to the previous VMA even though we
+ * have, through the use of the just_expand flag, indicated we do not
+ * need to do so.
+ */
+ alloc_and_link_vma(&mm, 0, 0x2000, 0, vma_flags);
+
+ /*
+ * We will be positioned at the prev VMA, but looking to expand to
+ * 0x9000.
+ */
+ vma_iter_set(&vmi, 0x3000);
+ vma_prev = alloc_and_link_vma(&mm, 0x3000, 0x5000, 3, vma_flags);
+ vmg.prev = vma_prev;
+ vmg.just_expand = true;
+
+ vma = vma_merge_new_range(&vmg);
+ ASSERT_NE(vma, NULL);
+ ASSERT_EQ(vma, vma_prev);
+ ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
+ ASSERT_EQ(vma->vm_start, 0x3000);
+ ASSERT_EQ(vma->vm_end, 0x9000);
+ ASSERT_EQ(vma_start_pgoff(vma), 3);
+ ASSERT_EQ(vma_start_anon_pgoff(vma), 3);
+ ASSERT_TRUE(vma_write_started(vma));
+ ASSERT_EQ(vma_iter_addr(&vmi), 0x3000);
+ vma_assert_attached(vma);
+
+ cleanup_mm(&mm, &vmi);
+ return true;
+}
+
+static void run_merge_tests(int *num_tests, int *num_fail)
+{
+ /* Very simple tests to kick the tyres. */
+ TEST(simple_merge);
+ TEST(simple_modify);
+ TEST(simple_expand);
+ TEST(simple_shrink);
+
+ TEST(merge_new);
+ TEST(vma_merge_special_flags);
+ TEST(vma_merge_with_close);
+ TEST(vma_merge_new_with_close);
+ TEST(merge_existing);
+ TEST(anon_vma_non_mergeable);
+ TEST(dup_anon_vma);
+ TEST(vmi_prealloc_fail);
+ TEST(merge_extend);
+ TEST(expand_only_mode);
+}
diff --git a/tools/testing/vma/tests/mmap.c b/tools/testing/vma/tests/mmap.c
new file mode 100644
index 000000000..c85bc000d
--- /dev/null
+++ b/tools/testing/vma/tests/mmap.c
@@ -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);
+}
diff --git a/tools/testing/vma/tests/vma.c b/tools/testing/vma/tests/vma.c
new file mode 100644
index 000000000..c8ef7b8cd
--- /dev/null
+++ b/tools/testing/vma/tests/vma.c
@@ -0,0 +1,700 @@
+// 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_prev, *vma_new, *vma_next, *vma_orig;
+
+ /* Move forwards, adjacent to old self - self-merge. */
+
+ vma = alloc_and_link_vma(&mm, 0x1000, 0x2000, 1, vma_flags);
+ vma_set_anonymous(vma);
+ vma_orig = vma;
+ vma_new = copy_vma(&vma, 0x2000, 0x1000, 1, 1, &need_locks);
+ ASSERT_EQ(vma_new, vma_orig);
+ ASSERT_EQ(vma, vma_orig);
+ ASSERT_EQ(vma_new->vm_start, 0x1000);
+ ASSERT_EQ(vma_new->vm_end, 0x3000);
+
+ cleanup_mm(&mm, &vmi);
+
+ /* Move backwards, adjacent to old self - self-merge. */
+
+ vma = alloc_and_link_vma(&mm, 0x2000, 0x3000, 2, vma_flags);
+ vma_set_anonymous(vma);
+ vma_orig = vma;
+ vma_new = copy_vma(&vma, 0x1000, 0x1000, 2, 2, &need_locks);
+ ASSERT_EQ(vma_new, vma_orig);
+ ASSERT_EQ(vma, vma_orig);
+ ASSERT_EQ(vma_new->vm_start, 0x1000);
+ ASSERT_EQ(vma_new->vm_end, 0x3000);
+
+ cleanup_mm(&mm, &vmi);
+
+ /*
+ * Move backwards between prior VMA and old self - self-merge and vma
+ * updated to a new VMA.
+ */
+
+ vma_prev = alloc_and_link_vma(&mm, 0x1000, 0x2000, 1, vma_flags);
+ vma_set_anonymous(vma_prev);
+ vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vma_flags);
+ vma_set_anonymous(vma);
+ vma_orig = vma;
+ vma_new = copy_vma(&vma, 0x2000, 0x1000, 3, 3, &need_locks);
+ ASSERT_NE(vma_new, vma_orig);
+ ASSERT_EQ(vma_new, vma);
+ ASSERT_EQ(vma_new->vm_start, 0x1000);
+ ASSERT_EQ(vma_new->vm_end, 0x4000);
+
+ cleanup_mm(&mm, &vmi);
+
+ /* 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, 3, &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, 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);
+}