While maintaining virtual page offsets for VMAs has no impact for most
merge cases, it does impact MAP_PRIVATE-mapped file-backed mappings which
happen to have matching page offset but not matching virtual page offset.

Assert this behaviour by attempting to map an unfaulted MAP_PRIVATE-memfd
region with a faulted one with compatible file page offsets but
incompatible virtual page offsets.

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

diff --git a/tools/testing/selftests/mm/merge.c 
b/tools/testing/selftests/mm/merge.c
index 519e5ac02db7..c093e27d0aea 100644
--- a/tools/testing/selftests/mm/merge.c
+++ b/tools/testing/selftests/mm/merge.c
@@ -1305,6 +1305,63 @@ TEST_F(merge, merge_vmas_with_mseal)
        ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size);
 }
 
+TEST_F(merge, virt_and_page_offset_mismatch_memfd)
+{
+       struct procmap_fd *procmap = &self->procmap;
+       unsigned int page_size = self->page_size;
+       char *carveout = self->carveout;
+       char *ptr, *ptr2;
+       int fd;
+
+       /* Create a 10 page memfd descriptor. */
+       fd = memfd_create("virt_page_offset_test", MFD_CLOEXEC);
+       ASSERT_NE(fd, -1);
+       ASSERT_EQ(ftruncate(fd, 10 * page_size), 0);
+
+       /* Map a region using the memfd at page offset 0. */
+       ptr = mmap(carveout, 5 * page_size, PROT_READ | PROT_WRITE,
+                  MAP_FIXED | MAP_PRIVATE, fd, 0);
+       ASSERT_NE(ptr, MAP_FAILED);
+
+       /*
+        * Map another separately, and fault in, at page offset 5:
+        *
+        * |-----------|           |---------|
+        * | unfaulted |           | faulted |
+        * |-----------|           |---------|
+        */
+       ptr2 = mmap(&carveout[10 * page_size], 5 * page_size,
+                   PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE,
+                   fd, 5 * page_size);
+       ASSERT_NE(ptr2, MAP_FAILED);
+       ptr2[0] = 'x';
+
+       /*
+        * Now move it in place:
+        *
+        *                   |----------|
+        *                   |          |
+        *                   v          |
+        * |-----------|           |---------|
+        * | unfaulted |           | faulted |
+        * |-----------|           |---------|
+        *
+        * Because virtual page offset of the faulted region is now
+        * &carveout[10 * page_size], despite the two regions being mergeable
+        * due to file page offset, they are NOT mergeable due to virtual page
+        * offset.
+        */
+       ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
+                         MREMAP_MAYMOVE | MREMAP_FIXED,
+                         &carveout[5 * page_size]);
+       ASSERT_NE(ptr2, MAP_FAILED);
+
+       /* Assert that they did not merge. */
+       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 + 5 * 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