[PATCH] selftests/mm: merge: reserve the far mremap() targets

From: Danish Khateeb

Date: Tue Oct 20 2026 - 21:19:45 EST


The merge tests map their areas at fixed addresses within a 30-page
PROT_NONE carveout. Several mremap() tests move an area 1000 or 2000
pages away to separate it from the others, but those targets are outside
the carveout, and MREMAP_FIXED unmaps whatever is mapped there.

Under virtme-ng, the shared libraries are packed right above the
carveout and the dynamic linker starts about 4 MiB above it. The moves
then replace part of its text with an anonymous mapping, and the test
crashes in its next lazily bound call:

merge[6980]: segfault at 7f7ebb3c1e60 ip 00007f7ebb3c1e60 sp 00007ffd35fbaec8 error 14

Two to four of the mremap() tests then fail with "Test terminated
unexpectedly by signal 11", depending on the exact layout. On a host
where libc is mapped at a 2 MiB aligned address, the targets happen to
land above the dynamic linker and the tests pass.

Make the carveout large enough to contain the targets.

Fixes: 4e25f85b9f85 ("tools/testing/selftests: add mremap() unfaulted/faulted test cases")
Signed-off-by: Danish Khateeb <danishkhateeb03@xxxxxxxxx>
---

Notes:
Tested under virtme-ng (9p root, glibc 2.44) on next-20261020 with a
KASAN/lockdep kernel, three runs of each binary:

merge before: pass:22 fail:4 skip:1 (SIGSEGV in ld-linux-x86-64.so.2)
merge after: pass:26 fail:0 skip:1

A /proc/self/maps dump at the first far move shows the target inside
the text of ld-linux-x86-64.so.2. The same tests crash under virtme-ng
with v7.2 and v7.3-rc7 kernels, so this is not caused by a kernel
change. On the host (glibc 2.44, libc mapped 2 MiB aligned) the results
are the same with and without the patch.

An LLM was used to help with this patch.

tools/testing/selftests/mm/merge.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c
index 7dd2933417c1..8291c208f0ae 100644
--- a/tools/testing/selftests/mm/merge.c
+++ b/tools/testing/selftests/mm/merge.c
@@ -22,9 +22,17 @@ FIXTURE(merge)
struct procmap_fd procmap;
};

+/*
+ * Some tests move a mapping 1000 or 2000 pages away with MREMAP_FIXED, which
+ * unmaps whatever is mapped at the target, so make the carveout cover those
+ * targets too. Otherwise the test can unmap its own code, such as the text of
+ * the dynamic linker.
+ */
+#define CARVEOUT_PAGES 2048
+
static char *map_carveout(unsigned int page_size)
{
- return mmap(NULL, 30 * page_size, PROT_NONE,
+ return mmap(NULL, CARVEOUT_PAGES * page_size, PROT_NONE,
MAP_ANON | MAP_PRIVATE, -1, 0);
}

@@ -73,7 +81,7 @@ FIXTURE_SETUP(merge)

FIXTURE_TEARDOWN(merge)
{
- ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
+ ASSERT_EQ(munmap(self->carveout, CARVEOUT_PAGES * self->page_size), 0);
/* May fail for parent of forked process. */
close_procmap(&self->procmap);
/*
@@ -115,7 +123,7 @@ FIXTURE_SETUP(merge_with_fork)

FIXTURE_TEARDOWN(merge_with_fork)
{
- ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0);
+ ASSERT_EQ(munmap(self->carveout, CARVEOUT_PAGES * self->page_size), 0);
ASSERT_EQ(close_procmap(&self->procmap), 0);
/* See above. */
prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0);

base-commit: 59d7f9ec14ad4b6f38c04d254f8f2bfb991e888e
--
2.56.0