Skip to content

Commit 19b65ff

Browse files
lorenzo-stoakesakpm00
authored andcommitted
selftests/mm: add fork CoW guard page test
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. [[email protected]: static process_madvise() wrapper for guard-pages] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Lorenzo Stoakes <[email protected]> Signed-off-by: Ryan Roberts <[email protected]> Reviewed-by: Liam R. Howlett <[email protected]> Cc: Jann Horn <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: Ryan Roberts <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 67c8b11 commit 19b65ff

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

tools/testing/selftests/mm/guard-pages.c

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ static int pidfd_open(pid_t pid, unsigned int flags)
5555
return syscall(SYS_pidfd_open, pid, flags);
5656
}
5757

58+
static ssize_t sys_process_madvise(int pidfd, const struct iovec *iovec,
59+
size_t n, int advice, unsigned int flags)
60+
{
61+
return syscall(__NR_process_madvise, pidfd, iovec, n, advice, flags);
62+
}
63+
5864
/*
5965
* Enable our signal catcher and try to read/write the specified buffer. The
6066
* return value indicates whether the read/write succeeds without a fatal
@@ -419,7 +425,7 @@ TEST_F(guard_pages, process_madvise)
419425
ASSERT_EQ(munmap(&ptr_region[99 * page_size], page_size), 0);
420426

421427
/* Now guard in one step. */
422-
count = process_madvise(pidfd, vec, 6, MADV_GUARD_INSTALL, 0);
428+
count = sys_process_madvise(pidfd, vec, 6, MADV_GUARD_INSTALL, 0);
423429

424430
/* OK we don't have permission to do this, skip. */
425431
if (count == -1 && errno == EPERM)
@@ -440,7 +446,7 @@ TEST_F(guard_pages, process_madvise)
440446
ASSERT_FALSE(try_read_write_buf(&ptr3[19 * page_size]));
441447

442448
/* Now do the same with unguard... */
443-
count = process_madvise(pidfd, vec, 6, MADV_GUARD_REMOVE, 0);
449+
count = sys_process_madvise(pidfd, vec, 6, MADV_GUARD_REMOVE, 0);
444450

445451
/* ...and everything should now succeed. */
446452

@@ -990,7 +996,7 @@ TEST_F(guard_pages, fork)
990996
MAP_ANON | MAP_PRIVATE, -1, 0);
991997
ASSERT_NE(ptr, MAP_FAILED);
992998

993-
/* Establish guard apges in the first 5 pages. */
999+
/* Establish guard pages in the first 5 pages. */
9941000
ASSERT_EQ(madvise(ptr, 5 * page_size, MADV_GUARD_INSTALL), 0);
9951001

9961002
pid = fork();
@@ -1029,6 +1035,77 @@ TEST_F(guard_pages, fork)
10291035
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
10301036
}
10311037

1038+
/*
1039+
* Assert expected behaviour after we fork populated ranges of anonymous memory
1040+
* and then guard and unguard the range.
1041+
*/
1042+
TEST_F(guard_pages, fork_cow)
1043+
{
1044+
const unsigned long page_size = self->page_size;
1045+
char *ptr;
1046+
pid_t pid;
1047+
int i;
1048+
1049+
/* Map 10 pages. */
1050+
ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
1051+
MAP_ANON | MAP_PRIVATE, -1, 0);
1052+
ASSERT_NE(ptr, MAP_FAILED);
1053+
1054+
/* Populate range. */
1055+
for (i = 0; i < 10 * page_size; i++) {
1056+
char chr = 'a' + (i % 26);
1057+
1058+
ptr[i] = chr;
1059+
}
1060+
1061+
pid = fork();
1062+
ASSERT_NE(pid, -1);
1063+
if (!pid) {
1064+
/* This is the child process now. */
1065+
1066+
/* Ensure the range is as expected. */
1067+
for (i = 0; i < 10 * page_size; i++) {
1068+
char expected = 'a' + (i % 26);
1069+
char actual = ptr[i];
1070+
1071+
ASSERT_EQ(actual, expected);
1072+
}
1073+
1074+
/* Establish guard pages across the whole range. */
1075+
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0);
1076+
/* Remove it. */
1077+
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
1078+
1079+
/*
1080+
* By removing the guard pages, the page tables will be
1081+
* cleared. Assert that we are looking at the zero page now.
1082+
*/
1083+
for (i = 0; i < 10 * page_size; i++) {
1084+
char actual = ptr[i];
1085+
1086+
ASSERT_EQ(actual, '\0');
1087+
}
1088+
1089+
exit(0);
1090+
}
1091+
1092+
/* Parent process. */
1093+
1094+
/* Parent simply waits on child. */
1095+
waitpid(pid, NULL, 0);
1096+
1097+
/* Ensure the range is unchanged in parent anon range. */
1098+
for (i = 0; i < 10 * page_size; i++) {
1099+
char expected = 'a' + (i % 26);
1100+
char actual = ptr[i];
1101+
1102+
ASSERT_EQ(actual, expected);
1103+
}
1104+
1105+
/* Cleanup. */
1106+
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
1107+
}
1108+
10321109
/*
10331110
* Assert that forking a process with VMAs that do have VM_WIPEONFORK set
10341111
* behave as expected.

tools/testing/selftests/mm/run_vmtests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ separated by spaces:
4545
vmalloc smoke tests
4646
- hmm
4747
hmm smoke tests
48+
- madv_guard
49+
test madvise(2) MADV_GUARD_INSTALL and MADV_GUARD_REMOVE options
4850
- madv_populate
4951
test memadvise(2) MADV_POPULATE_{READ,WRITE} options
5052
- memfd_secret
@@ -375,6 +377,9 @@ CATEGORY="mremap" run_test ./mremap_dontunmap
375377

376378
CATEGORY="hmm" run_test bash ./test_hmm.sh smoke
377379

380+
# MADV_GUARD_INSTALL and MADV_GUARD_REMOVE tests
381+
CATEGORY="madv_guard" run_test ./guard-pages
382+
378383
# MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
379384
CATEGORY="madv_populate" run_test ./madv_populate
380385

0 commit comments

Comments
 (0)