linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] selftests/mm: add fork CoW guard page test
@ 2024-12-05 19:07 Lorenzo Stoakes
  2024-12-05 19:19 ` Liam R. Howlett
  0 siblings, 1 reply; 2+ messages in thread
From: Lorenzo Stoakes @ 2024-12-05 19:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Shuah Khan, linux-mm, linux-kselftest, linux-kernel,
	Liam Howlett, Vlastimil Babka, Jann Horn

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@oracle.com>
---
 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


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] selftests/mm: add fork CoW guard page test
  2024-12-05 19:07 [PATCH] selftests/mm: add fork CoW guard page test Lorenzo Stoakes
@ 2024-12-05 19:19 ` Liam R. Howlett
  0 siblings, 0 replies; 2+ messages in thread
From: Liam R. Howlett @ 2024-12-05 19:19 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Andrew Morton, Shuah Khan, linux-mm, linux-kselftest,
	linux-kernel, Vlastimil Babka, Jann Horn

* Lorenzo Stoakes <lorenzo.stoakes@oracle.com> [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@oracle.com>

Nice to see some more testing going in.

Reviewed-by: Liam R. Howlett <Liam.Howlett@Oracle.com>

> ---
>  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


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2024-12-05 19:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-05 19:07 [PATCH] selftests/mm: add fork CoW guard page test Lorenzo Stoakes
2024-12-05 19:19 ` Liam R. Howlett

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox