aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hildenbrand <[email protected]>2022-11-08 18:46:52 +0100
committerAndrew Morton <[email protected]>2022-11-30 15:58:49 -0800
commit07f8bac4982f98fc4b5ae05679d76fccc15079ea (patch)
tree4d180889ab9fe471d23ff203a7d9f8bca9c02da7
parentd6379159f47630813f06f97535cc82ce7b9eed49 (diff)
selftests/vm: anon_cow: add mprotect() optimization tests
Let's extend the test to cover the possible mprotect() optimization when removing write-protection. mprotect() must not allow write-access to a COW-shared page by accident. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: David Hildenbrand <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Anshuman Khandual <[email protected]> Cc: Dave Chinner <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Nadav Amit <[email protected]> Cc: Nicholas Piggin <[email protected]> Cc: Peter Xu <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
-rw-r--r--tools/testing/selftests/vm/anon_cow.c49
1 files changed, 46 insertions, 3 deletions
diff --git a/tools/testing/selftests/vm/anon_cow.c b/tools/testing/selftests/vm/anon_cow.c
index 705bd0b3db11..bbb251eb5025 100644
--- a/tools/testing/selftests/vm/anon_cow.c
+++ b/tools/testing/selftests/vm/anon_cow.c
@@ -190,7 +190,8 @@ static int child_vmsplice_memcmp_fn(char *mem, size_t size,
typedef int (*child_fn)(char *mem, size_t size, struct comm_pipes *comm_pipes);
-static void do_test_cow_in_parent(char *mem, size_t size, child_fn fn)
+static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect,
+ child_fn fn)
{
struct comm_pipes comm_pipes;
char buf;
@@ -212,6 +213,22 @@ static void do_test_cow_in_parent(char *mem, size_t size, child_fn fn)
while (read(comm_pipes.child_ready[0], &buf, 1) != 1)
;
+
+ if (do_mprotect) {
+ /*
+ * mprotect() optimizations might try avoiding
+ * write-faults by directly mapping pages writable.
+ */
+ ret = mprotect(mem, size, PROT_READ);
+ ret |= mprotect(mem, size, PROT_READ|PROT_WRITE);
+ if (ret) {
+ ksft_test_result_fail("mprotect() failed\n");
+ write(comm_pipes.parent_ready[1], "0", 1);
+ wait(&ret);
+ goto close_comm_pipes;
+ }
+ }
+
/* Modify the page. */
memset(mem, 0xff, size);
write(comm_pipes.parent_ready[1], "0", 1);
@@ -229,12 +246,22 @@ close_comm_pipes:
static void test_cow_in_parent(char *mem, size_t size)
{
- do_test_cow_in_parent(mem, size, child_memcmp_fn);
+ do_test_cow_in_parent(mem, size, false, child_memcmp_fn);
+}
+
+static void test_cow_in_parent_mprotect(char *mem, size_t size)
+{
+ do_test_cow_in_parent(mem, size, true, child_memcmp_fn);
}
static void test_vmsplice_in_child(char *mem, size_t size)
{
- do_test_cow_in_parent(mem, size, child_vmsplice_memcmp_fn);
+ do_test_cow_in_parent(mem, size, false, child_vmsplice_memcmp_fn);
+}
+
+static void test_vmsplice_in_child_mprotect(char *mem, size_t size)
+{
+ do_test_cow_in_parent(mem, size, true, child_vmsplice_memcmp_fn);
}
static void do_test_vmsplice_in_parent(char *mem, size_t size,
@@ -970,6 +997,14 @@ static const struct test_case test_cases[] = {
test_cow_in_parent,
},
/*
+ * Basic test, but do an additional mprotect(PROT_READ)+
+ * mprotect(PROT_READ|PROT_WRITE) in the parent before write access.
+ */
+ {
+ "Basic COW after fork() with mprotect() optimization",
+ test_cow_in_parent_mprotect,
+ },
+ /*
* vmsplice() [R/O GUP] + unmap in the child; modify in the parent. If
* we miss to break COW, the child observes modifications by the parent.
* This is CVE-2020-29374 reported by Jann Horn.
@@ -979,6 +1014,14 @@ static const struct test_case test_cases[] = {
test_vmsplice_in_child
},
/*
+ * vmsplice() test, but do an additional mprotect(PROT_READ)+
+ * mprotect(PROT_READ|PROT_WRITE) in the parent before write access.
+ */
+ {
+ "vmsplice() + unmap in child with mprotect() optimization",
+ test_vmsplice_in_child_mprotect
+ },
+ /*
* vmsplice() [R/O GUP] in parent before fork(), unmap in parent after
* fork(); modify in the child. If we miss to break COW, the parent
* observes modifications by the child.