Assert that MAP_PRIVATE-mapped /dev/zero mappings behave like they are
anonymous.

We test both unfaulted and faulted/unfaulted merges - each with the regions
having page offset of 0, which would not merge if the mappings were treated
as if they were file-backed.

With the recent change that makes them behave as pure anonymous mappings,
the merges should succeed as their page offsets are equal to their virtual
page offsets.

Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]>
---
 tools/testing/selftests/mm/merge.c | 104 +++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/tools/testing/selftests/mm/merge.c 
b/tools/testing/selftests/mm/merge.c
index c093e27d0aea..edfbd39ae58e 100644
--- a/tools/testing/selftests/mm/merge.c
+++ b/tools/testing/selftests/mm/merge.c
@@ -1362,6 +1362,110 @@ TEST_F(merge, virt_and_page_offset_mismatch_memfd)
        ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 5 * page_size);
 }
 
+TEST_F(merge, merge_map_private_dev_zero_unfaulted)
+{
+       struct procmap_fd *procmap = &self->procmap;
+       unsigned int page_size = self->page_size;
+       char *carveout = self->carveout;
+       char *ptr, *ptr2;
+       int fd_zero;
+
+       if (access("/dev/zero", F_OK))
+               SKIP(return, "No /dev/zero.");
+       fd_zero = open("/dev/zero", O_RDWR);
+       ASSERT_NE(fd_zero, -1);
+
+       /*
+        * Map two MAP_PRIVATE-/dev/zero VMAs next to one another with offset 0
+        * each.
+        *
+        * With these being made truly anonymous upon mapping, they will
+        * merge. If they were file-backed VMAs the page offsets would prevent
+        * merge:
+        *
+        * |-----||------|    |-------------|
+        * | ptr || ptr2 | -> |     ptr     |
+        * |-----||------|    |-------------|
+        */
+       ptr = mmap(carveout, 5 * page_size, PROT_READ | PROT_WRITE,
+                  MAP_FIXED | MAP_PRIVATE, fd_zero, 0);
+       if (ptr == MAP_FAILED) {
+               close(fd_zero);
+               ASSERT_TRUE(false);
+       }
+       ptr2 = mmap(&carveout[5 * page_size], 5 * page_size,
+                  PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, fd_zero, 0);
+       if (ptr2 == MAP_FAILED) {
+               close(fd_zero);
+               ASSERT_TRUE(false);
+       }
+       close(fd_zero);
+
+       /* Assert that they merged. */
+       ASSERT_TRUE(find_vma_procmap(procmap, ptr));
+       ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
+       ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size);
+}
+
+TEST_F(merge, merge_map_private_dev_zero_faulted_unfaulted)
+{
+       struct procmap_fd *procmap = &self->procmap;
+       unsigned int page_size = self->page_size;
+       char *carveout = self->carveout;
+       char *ptr, *ptr2;
+       int fd_zero;
+
+       if (access("/dev/zero", F_OK))
+               SKIP(return, "No /dev/zero.");
+       fd_zero = open("/dev/zero", O_RDWR);
+       ASSERT_NE(fd_zero, -1);
+
+       /*
+        * Map a MAP_PRIVATE mapping of /dev/zero with page offset 0, then fault
+        * it in:
+        *
+        * |-------------------------------|
+        * |           faulted             |
+        * |-------------------------------|
+        */
+       ptr = mmap(carveout, 15 * page_size, PROT_READ | PROT_WRITE,
+                  MAP_FIXED | MAP_PRIVATE, fd_zero, 0);
+       if (ptr == MAP_FAILED) {
+               close(fd_zero);
+               ASSERT_TRUE(false);
+       }
+       memset(ptr, 'x', 15 * page_size);
+
+       /*
+        * Unmap the middle:
+        *
+        * |---------|           |---------|
+        * | faulted |           | faulted |
+        * |---------|           |---------|
+        */
+       ASSERT_EQ(munmap(&ptr[5 * page_size], 5 * page_size), 0);
+
+       /*
+        * Map in a new unfaulted mapping in the middle with page offset 0 -
+        * this should merge and would not if it were treated as a file rather
+        * than pure anon:
+        *
+        * |---------|-----------|---------|
+        * | faulted | unfaulted | faulted |
+        * |---------|-----------|---------|
+        */
+       ptr2 = mmap(&carveout[5 * page_size], 5 * page_size,
+                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE,
+                   fd_zero, 0);
+       close(fd_zero);
+       ASSERT_NE(ptr2, MAP_FAILED);
+
+       /* Assert that they merged. */
+       ASSERT_TRUE(find_vma_procmap(procmap, ptr));
+       ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr);
+       ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size);
+}
+
 TEST_F(merge_with_fork, mremap_faulted_to_unfaulted_prev)
 {
        struct procmap_fd *procmap = &self->procmap;

-- 
2.55.0


Reply via email to