On Wed, Oct 29, 2025 at 9:51 AM Lorenzo Stoakes <[email protected]> wrote: > > Assert that we observe guard regions appearing in /proc/$pid/smaps as > expected, and when split/merge is performed too (with expected sticky > behaviour). > > Also add handling for file systems which don't sanely handle mmap() VMA > merging so we don't incorrectly encounter a test failure in this situation. > > Signed-off-by: Lorenzo Stoakes <[email protected]>
Reviewed-by: Suren Baghdasaryan <[email protected]> > --- > tools/testing/selftests/mm/guard-regions.c | 120 +++++++++++++++++++++ > tools/testing/selftests/mm/vm_util.c | 5 + > tools/testing/selftests/mm/vm_util.h | 1 + > 3 files changed, 126 insertions(+) > > diff --git a/tools/testing/selftests/mm/guard-regions.c > b/tools/testing/selftests/mm/guard-regions.c > index 8dd81c0a4a5a..a9be11e03a6a 100644 > --- a/tools/testing/selftests/mm/guard-regions.c > +++ b/tools/testing/selftests/mm/guard-regions.c > @@ -94,6 +94,7 @@ static void *mmap_(FIXTURE_DATA(guard_regions) * self, > case ANON_BACKED: > flags |= MAP_PRIVATE | MAP_ANON; > fd = -1; > + offset = 0; > break; > case SHMEM_BACKED: > case LOCAL_FILE_BACKED: > @@ -260,6 +261,54 @@ static bool is_buf_eq(char *buf, size_t size, char chr) > return true; > } > > +/* > + * Some file systems have issues with merging due to changing merge-sensitive > + * parameters in the .mmap callback, and prior to .mmap_prepare being > + * implemented everywhere this will now result in an unexpected failure to > + * merge (e.g. - overlayfs). > + * > + * Perform a simple test to see if the local file system suffers from this, > if > + * it does then we can skip test logic that assumes local file system > merging is > + * sane. > + */ > +static bool local_fs_has_sane_mmap(FIXTURE_DATA(guard_regions) * self, > + const FIXTURE_VARIANT(guard_regions) * > variant) > +{ > + const unsigned long page_size = self->page_size; > + char *ptr, *ptr2; > + struct procmap_fd procmap; > + > + if (variant->backing != LOCAL_FILE_BACKED) > + return true; > + > + /* Map 10 pages. */ > + ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ | > PROT_WRITE, 0, 0); > + if (ptr == MAP_FAILED) > + return false; > + /* Unmap the middle. */ > + munmap(&ptr[5 * page_size], page_size); > + > + /* Map again. */ > + ptr2 = mmap_(self, variant, &ptr[5 * page_size], page_size, PROT_READ > | PROT_WRITE, > + MAP_FIXED, 5 * page_size); > + > + if (ptr2 == MAP_FAILED) > + return false; > + > + /* Now make sure they all merged. */ > + if (open_self_procmap(&procmap) != 0) > + return false; > + if (!find_vma_procmap(&procmap, ptr)) > + return false; > + if (procmap.query.vma_start != (unsigned long)ptr) > + return false; > + if (procmap.query.vma_end != (unsigned long)ptr + 10 * page_size) > + return false; > + close_procmap(&procmap); > + > + return true; > +} > + > FIXTURE_SETUP(guard_regions) > { > self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); > @@ -2138,4 +2187,75 @@ TEST_F(guard_regions, pagemap_scan) > ASSERT_EQ(munmap(ptr, 10 * page_size), 0); > } > > +TEST_F(guard_regions, smaps) > +{ > + const unsigned long page_size = self->page_size; > + struct procmap_fd procmap; > + char *ptr, *ptr2; > + int i; > + > + /* Map a region. */ > + ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ | > PROT_WRITE, 0, 0); > + ASSERT_NE(ptr, MAP_FAILED); > + > + /* We shouldn't yet see a guard flag. */ > + ASSERT_FALSE(check_vmflag_guard(ptr)); > + > + /* Install a single guard region. */ > + ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); > + > + /* Now we should see a guard flag. */ > + ASSERT_TRUE(check_vmflag_guard(ptr)); > + > + /* > + * Removing the guard region should not change things because we > simply > + * cannot accurately track whether a given VMA has had all of its > guard > + * regions removed. > + */ > + ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_REMOVE), 0); > + ASSERT_TRUE(check_vmflag_guard(ptr)); > + > + /* Install guard regions throughout. */ > + for (i = 0; i < 10; i++) { > + ASSERT_EQ(madvise(&ptr[i * page_size], page_size, > MADV_GUARD_INSTALL), 0); > + /* We should always see the guard region flag. */ > + ASSERT_TRUE(check_vmflag_guard(ptr)); > + } > + > + /* Split into two VMAs. */ > + ASSERT_EQ(munmap(&ptr[4 * page_size], page_size), 0); > + > + /* Both VMAs should have the guard flag set. */ > + ASSERT_TRUE(check_vmflag_guard(ptr)); > + ASSERT_TRUE(check_vmflag_guard(&ptr[5 * page_size])); > + > + /* > + * If the local file system is unable to merge VMAs due to having > + * unusual characteristics, there is no point in asserting merge > + * behaviour. > + */ > + if (!local_fs_has_sane_mmap(self, variant)) { > + TH_LOG("local filesystem does not support sane merging > skipping merge test"); > + return; > + } > + > + /* Map a fresh VMA between the two split VMAs. */ > + ptr2 = mmap_(self, variant, &ptr[4 * page_size], page_size, > + PROT_READ | PROT_WRITE, MAP_FIXED, 4 * page_size); > + ASSERT_NE(ptr2, MAP_FAILED); > + > + /* > + * Check the procmap to ensure that this VMA merged with the adjacent > + * two. The guard region flag is 'sticky' so should not preclude > + * merging. > + */ > + ASSERT_EQ(open_self_procmap(&procmap), 0); > + 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); > + ASSERT_EQ(close_procmap(&procmap), 0); > + /* And, of course, this VMA should have the guard flag set. */ > + ASSERT_TRUE(check_vmflag_guard(ptr)); > +} > + > TEST_HARNESS_MAIN > diff --git a/tools/testing/selftests/mm/vm_util.c > b/tools/testing/selftests/mm/vm_util.c > index e33cda301dad..605cb58ea5c3 100644 > --- a/tools/testing/selftests/mm/vm_util.c > +++ b/tools/testing/selftests/mm/vm_util.c > @@ -449,6 +449,11 @@ bool check_vmflag_pfnmap(void *addr) > return check_vmflag(addr, "pf"); > } > > +bool check_vmflag_guard(void *addr) > +{ > + return check_vmflag(addr, "gu"); > +} > + > bool softdirty_supported(void) > { > char *addr; > diff --git a/tools/testing/selftests/mm/vm_util.h > b/tools/testing/selftests/mm/vm_util.h > index 26c30fdc0241..a8abdf414d46 100644 > --- a/tools/testing/selftests/mm/vm_util.h > +++ b/tools/testing/selftests/mm/vm_util.h > @@ -98,6 +98,7 @@ int uffd_register_with_ioctls(int uffd, void *addr, > uint64_t len, > unsigned long get_free_hugepages(void); > bool check_vmflag_io(void *addr); > bool check_vmflag_pfnmap(void *addr); > +bool check_vmflag_guard(void *addr); > int open_procmap(pid_t pid, struct procmap_fd *procmap_out); > int query_procmap(struct procmap_fd *procmap); > bool find_vma_procmap(struct procmap_fd *procmap, void *address); > -- > 2.51.0 >
