Commit 07f8bac4 authored by David Hildenbrand's avatar David Hildenbrand Committed by Andrew Morton
Browse files

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/20221108174652.198904-8-david@redhat.com


Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Nadav Amit <namit@vmware.com>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent d6379159
Loading
Loading
Loading
Loading
+46 −3
Original line number Diff line number Diff line
@@ -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 @@ static void do_test_cow_in_parent(char *mem, size_t size, child_fn fn)

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,
@@ -969,6 +996,14 @@ static const struct test_case test_cases[] = {
		"Basic COW after fork()",
		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.
@@ -978,6 +1013,14 @@ static const struct test_case test_cases[] = {
		"vmsplice() + unmap in child",
		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