* Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx> [241205 14:07]: > When we fork anonymous pages, apply a guard page then remove it, the > previous CoW mapping is cleared. > > This might not be obvious to an outside observer without taking some time > to think about how the overall process functions, so document that this is > the case through a test, which also usefully asserts that the behaviour is > as we expect. > > This is grouped with other, more important, fork tests that ensure that > guard pages are correctly propagated on fork. > > Fix a typo in a nearby comment at the same time. > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx> Nice to see some more testing going in. Reviewed-by: Liam R. Howlett <Liam.Howlett@xxxxxxxxxx> > --- > tools/testing/selftests/mm/guard-pages.c | 73 +++++++++++++++++++++++- > 1 file changed, 72 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/mm/guard-pages.c b/tools/testing/selftests/mm/guard-pages.c > index 7cdf815d0d63..d8f8dee9ebbd 100644 > --- a/tools/testing/selftests/mm/guard-pages.c > +++ b/tools/testing/selftests/mm/guard-pages.c > @@ -990,7 +990,7 @@ TEST_F(guard_pages, fork) > MAP_ANON | MAP_PRIVATE, -1, 0); > ASSERT_NE(ptr, MAP_FAILED); > > - /* Establish guard apges in the first 5 pages. */ > + /* Establish guard pages in the first 5 pages. */ > ASSERT_EQ(madvise(ptr, 5 * page_size, MADV_GUARD_INSTALL), 0); > > pid = fork(); > @@ -1029,6 +1029,77 @@ TEST_F(guard_pages, fork) > ASSERT_EQ(munmap(ptr, 10 * page_size), 0); > } > > +/* > + * Assert expected behaviour after we fork populated ranges of anonymous memory > + * and then guard and unguard the range. > + */ > +TEST_F(guard_pages, fork_cow) > +{ > + const unsigned long page_size = self->page_size; > + char *ptr; > + pid_t pid; > + int i; > + > + /* Map 10 pages. */ > + ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, > + MAP_ANON | MAP_PRIVATE, -1, 0); > + ASSERT_NE(ptr, MAP_FAILED); > + > + /* Populate range. */ > + for (i = 0; i < 10 * page_size; i++) { > + char chr = 'a' + (i % 26); > + > + ptr[i] = chr; > + } > + > + pid = fork(); > + ASSERT_NE(pid, -1); > + if (!pid) { > + /* This is the child process now. */ > + > + /* Ensure the range is as expected. */ > + for (i = 0; i < 10 * page_size; i++) { > + char expected = 'a' + (i % 26); > + char actual = ptr[i]; > + > + ASSERT_EQ(actual, expected); > + } > + > + /* Establish guard pages across the whole range. */ > + ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); > + /* Remove it. */ > + ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); > + > + /* > + * By removing the guard pages, the page tables will be > + * cleared. Assert that we are looking at the zero page now. > + */ > + for (i = 0; i < 10 * page_size; i++) { > + char actual = ptr[i]; > + > + ASSERT_EQ(actual, '\0'); > + } > + > + exit(0); > + } > + > + /* Parent process. */ > + > + /* Parent simply waits on child. */ > + waitpid(pid, NULL, 0); > + > + /* Ensure the range is unchanged in parent anon range. */ > + for (i = 0; i < 10 * page_size; i++) { > + char expected = 'a' + (i % 26); > + char actual = ptr[i]; > + > + ASSERT_EQ(actual, expected); > + } > + > + /* Cleanup. */ > + ASSERT_EQ(munmap(ptr, 10 * page_size), 0); > +} > + > /* > * Assert that forking a process with VMAs that do have VM_WIPEONFORK set > * behave as expected. > -- > 2.47.1