diff mbox series

[v1,2/2] kselftest: vm: add tests for memory-deny-write-execute

Message ID 20221026150457.36957-3-joey.gouly@arm.com (mailing list archive)
State New
Headers show
Series mm: In-kernel support for memory-deny-write-execute (MDWE) | expand

Commit Message

Joey Gouly Oct. 26, 2022, 3:04 p.m. UTC
Add some tests to cover the new PR_SET_MDWE prctl.

Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Cc: Shuah Khan <shuah@kernel.org>
---
 tools/testing/selftests/vm/mdwe_test.c | 194 +++++++++++++++++++++++++
 1 file changed, 194 insertions(+)
 create mode 100644 tools/testing/selftests/vm/mdwe_test.c

Comments

Mark Brown Oct. 28, 2022, 5:03 p.m. UTC | #1
On Wed, Oct 26, 2022 at 04:04:57PM +0100, Joey Gouly wrote:

> Add some tests to cover the new PR_SET_MDWE prctl.

Some comments below but they're all stylistic and let's not make perfect
be the enemy of the good here so

Reviewed-by: Mark Brown <broonie@kernel.org>

and we can iterate later rather than blocking anything on the testcase.

> +#ifdef __aarch64__
> +#define PROT_BTI      0x10            /* BTI guarded page */
> +#endif

We should get this from the kernel headers shouldn't we?  We generally
rely on things getting pulled in from there rather than locally
defining.

> +#define TEST1 "mmap(PROT_WRITE | PROT_EXEC)\n"
> +#define TEST2 "mmap(PROT_WRITE); mprotect(PROT_EXEC)\n"
> +#define TEST3 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_READ)\n"
> +#define TEST4 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_BTI)\n"

> +int test1(int mdwe_enabled)
> +{

It feels like we could usefully make an array of

	struct test {
		int (*run)(bool mdwe_enabled);
		char *name;
	}

then we'd need fewer ifdefs, things could be more usefully named and
it'd be a bit easier to add new cases.

> +#ifdef __aarch64__
> +	ksft_set_plan(12);
> +#else
> +	ksft_set_plan(9);
> +#endif

That'd just be ksft_test_plan(3 * ARRAY_SIZE(tests).

> +	// First run the tests without MDWE
> +	test_result(test1(0), TEST1);
> +	test_result(test2(0), TEST2);
> +	test_result(test3(0), TEST3);
> +#ifdef __aarch64__
> +	test_result(test4(0), TEST4);
> +#endif

and these calls to the tests would all be iterating over the array.
Kees Cook Oct. 28, 2022, 5:45 p.m. UTC | #2
On Wed, Oct 26, 2022 at 04:04:57PM +0100, Joey Gouly wrote:
> +#include "../kselftest.h"

I recommend using kselftest_harness.h instead; it provides much of the
infrastructure that is open-coded here. But yes, testing is good; thank
you! :)
Kees Cook Oct. 28, 2022, 8:16 p.m. UTC | #3
Here's an alternative rewritten to use kselftest_harness.h, with tests
for the prctl() flags, and the missed Makefile addition. This should be
much easier to add more variants and tests to, I hope.

-Kees

From bc442a99ebd9852bfaa7444b521bd55fdbb4d369 Mon Sep 17 00:00:00 2001
From: Kees Cook <keescook@chromium.org>
Date: Fri, 28 Oct 2022 13:10:45 -0700
Subject: [PATCH] selftests/vm: add tests for memory-deny-write-execute

Add tests for new prctl() commands, including flag values. Add tests for
new denials based on PROT_EXEC across mmap() and mprotect() with MDWE.

Co-developed-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 tools/testing/selftests/vm/Makefile    |   1 +
 tools/testing/selftests/vm/mdwe_test.c | 201 +++++++++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 tools/testing/selftests/vm/mdwe_test.c

diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index 163c2fde3cb3..8dd4d4910fa5 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -52,6 +52,7 @@ TEST_GEN_FILES += userfaultfd
 TEST_GEN_PROGS += soft-dirty
 TEST_GEN_PROGS += split_huge_page_test
 TEST_GEN_FILES += ksm_tests
+TEST_GEN_PROGS += mdwe_test
 
 ifeq ($(MACHINE),x86_64)
 CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_32bit_program.c -m32)
diff --git a/tools/testing/selftests/vm/mdwe_test.c b/tools/testing/selftests/vm/mdwe_test.c
new file mode 100644
index 000000000000..d6f6b751bcd6
--- /dev/null
+++ b/tools/testing/selftests/vm/mdwe_test.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifdef __aarch64__
+#include <asm/hwcap.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <linux/prctl.h>
+
+#include "../kselftest_harness.h"
+
+#define PR_SET_MDWE                     65
+# define PR_MDWE_FLAG_MMAP              1
+
+#define PR_GET_MDWE                     66
+
+#ifdef __aarch64__
+# define PROT_BTI	0x10            /* BTI guarded page */
+#else
+# define PROT_BTI	0
+#endif
+
+TEST(prctl_flags)
+{
+	EXPECT_LT(prctl(PR_SET_MDWE, 7, 0, 0, 0), 0);
+	EXPECT_LT(prctl(PR_SET_MDWE, 0, 7, 0, 0), 0);
+	EXPECT_LT(prctl(PR_SET_MDWE, 0, 0, 7, 0), 0);
+	EXPECT_LT(prctl(PR_SET_MDWE, 0, 0, 0, 7), 0);
+
+	EXPECT_LT(prctl(PR_GET_MDWE, 7, 0, 0, 0), 0);
+	EXPECT_LT(prctl(PR_GET_MDWE, 0, 7, 0, 0), 0);
+	EXPECT_LT(prctl(PR_GET_MDWE, 0, 0, 7, 0), 0);
+	EXPECT_LT(prctl(PR_GET_MDWE, 0, 0, 0, 7), 0);
+}
+
+FIXTURE(mdwe)
+{
+	void *p;
+	int flags;
+        size_t size;
+	pid_t pid;
+};
+
+FIXTURE_VARIANT(mdwe)
+{
+	bool enabled;
+	bool forked;
+};
+
+FIXTURE_VARIANT_ADD(mdwe, stock)
+{
+        .enabled = false,
+	.forked = false,
+};
+
+FIXTURE_VARIANT_ADD(mdwe, enabled)
+{
+        .enabled = true,
+	.forked = false,
+};
+
+FIXTURE_VARIANT_ADD(mdwe, forked)
+{
+        .enabled = true,
+	.forked = true,
+};
+
+FIXTURE_SETUP(mdwe)
+{
+	int ret, status;
+
+	self->p = NULL;
+	self->flags = MAP_SHARED | MAP_ANONYMOUS;
+	self->size = getpagesize();
+
+	if (!variant->enabled)
+		return;
+
+	ret = prctl(PR_SET_MDWE, PR_MDWE_FLAG_MMAP, 0, 0, 0);
+	ASSERT_EQ(ret, 0) {
+		TH_LOG("PR_SET_MDWE failed or unsupported");
+	}
+
+	ret = prctl(PR_GET_MDWE, 0, 0, 0, 0);
+	ASSERT_EQ(ret, 1);
+
+	if (variant->forked) {
+		self->pid = fork();
+		ASSERT_GE(self->pid, 0) {
+			TH_LOG("fork failed\n");
+		}
+
+		if (self->pid > 0) {
+			ret = waitpid(self->pid, &status, 0);
+			ASSERT_TRUE(WIFEXITED(status));
+			exit(WEXITSTATUS(status));
+		}
+	}
+}
+
+FIXTURE_TEARDOWN(mdwe)
+{
+	if (self->p && self->p != MAP_FAILED)
+		munmap(self->p, self->size);
+}
+
+TEST_F(mdwe, mmap_READ_EXEC)
+{
+	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
+	EXPECT_NE(self->p, MAP_FAILED);
+}
+
+TEST_F(mdwe, mmap_WRITE_EXEC)
+{
+	self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
+	if (variant->enabled) {
+		EXPECT_EQ(self->p, MAP_FAILED);
+	} else {
+		EXPECT_NE(self->p, MAP_FAILED);
+	}
+}
+
+TEST_F(mdwe, mprotect_stay_EXEC)
+{
+	int ret;
+
+	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
+	ASSERT_NE(self->p, MAP_FAILED);
+
+	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
+	EXPECT_EQ(ret, 0);
+}
+
+TEST_F(mdwe, mprotect_add_EXEC)
+{
+	int ret;
+
+	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
+	ASSERT_NE(self->p, MAP_FAILED);
+
+	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
+	if (variant->enabled) {
+		EXPECT_LT(ret, 0);
+	} else {
+		EXPECT_EQ(ret, 0);
+	}
+}
+
+TEST_F(mdwe, mprotect_WRITE_EXEC)
+{
+	int ret;
+
+	self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
+	ASSERT_NE(self->p, MAP_FAILED);
+
+	ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
+	if (variant->enabled) {
+		EXPECT_LT(ret, 0);
+	} else {
+		EXPECT_EQ(ret, 0);
+	}
+}
+
+TEST_F(mdwe, mmap_FIXED)
+{
+	void *p;
+
+	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
+	ASSERT_NE(self->p, MAP_FAILED);
+
+	p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
+		 self->flags | MAP_FIXED, 0, 0);
+	if (variant->enabled) {
+		EXPECT_EQ(p, MAP_FAILED);
+	} else {
+		EXPECT_EQ(p, self->p);
+	}
+}
+
+TEST_F(mdwe, arm64_BTI)
+{
+	int ret;
+
+#ifdef __aarch64__
+	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
+#endif
+		SKIP(return, "HWCAP2_BTI not supported");
+
+	self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
+	ASSERT_NE(self->p, MAP_FAILED);
+
+	ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
+	EXPECT_EQ(ret, 0);
+}
+
+TEST_HARNESS_MAIN
Kees Cook Oct. 28, 2022, 8:19 p.m. UTC | #4
On Wed, Oct 26, 2022 at 04:04:57PM +0100, Joey Gouly wrote:
> [...]
> +# define PR_MDWE_FLAG_MMAP              1
> [...]
> +	// Enable MDWE and then run the tests again.
> +	ret = prctl(PR_SET_MDWE, PR_MDWE_FLAG_MMAP, 0, 0, 0);
> +	if (ret < 0) {
> +		ksft_print_msg("PR_SET_MDWE failed or unsupported!\n");
> +		goto exit;
> +	}
> +
> +	ret = prctl(PR_GET_MDWE, PR_MDWE_FLAG_MMAP, 0, 0, 0);
> +	if (ret == 0)
> +		ksft_exit_fail_msg("PR_GET_MDWE failed!");

This flag (PR_MDWE_FLAG_MMAP), while defined in uapi, wasn't actually
being used in the proposed prctl() api. :)
Szabolcs Nagy Nov. 7, 2022, 12:23 p.m. UTC | #5
The 10/28/2022 13:16, Kees Cook wrote:
> +++ b/tools/testing/selftests/vm/mdwe_test.c
> @@ -0,0 +1,201 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#ifdef __aarch64__
> +#include <asm/hwcap.h>
> +#endif
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/auxv.h>
> +#include <sys/mman.h>
> +#include <sys/prctl.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +#include <linux/prctl.h>
> +
> +#include "../kselftest_harness.h"
> +
> +#define PR_SET_MDWE                     65
> +# define PR_MDWE_FLAG_MMAP              1
> +
> +#define PR_GET_MDWE                     66
> +
> +#ifdef __aarch64__
> +# define PROT_BTI	0x10            /* BTI guarded page */
> +#else
> +# define PROT_BTI	0
> +#endif
> +
> +TEST(prctl_flags)
> +{
> +	EXPECT_LT(prctl(PR_SET_MDWE, 7, 0, 0, 0), 0);
> +	EXPECT_LT(prctl(PR_SET_MDWE, 0, 7, 0, 0), 0);
> +	EXPECT_LT(prctl(PR_SET_MDWE, 0, 0, 7, 0), 0);
> +	EXPECT_LT(prctl(PR_SET_MDWE, 0, 0, 0, 7), 0);

note that prctl is declared as

  int prctl(int, ...);

and all 4 arguments are documented to be unsigned long in
the linux man pages (even though some are pointers: this
is already a problem for the libc as it does not know if it
should use va_arg(ap, unsigned long) or va_arg(ap, void *),
in practice the call abi rules are the same for those on
linux, so either works unless the compiler deliberately
breaks the code due to the type mismatch ub).

passing an int where an unsigned long is needed is wrong: it
breaks va_arg rules on the c language level (posix rules too)
but more importantly it breaks abi rules: on most LP64 abis
it is not required to be signextended so arbitrary top 32bits
may be passed down.

so e.g.

  prctl(option, 0, 0, 0, 0);

should be written as

  prctl(option, 0L, 0L, 0L, 0L);

or similar (int signedness does not matter according to c
rules), otherwise non-zero top bits may be passed that the
kernel has to ignore, which it currently does not always do.

ideally the kernel updated all the prctl arg macros to have
type long or unsigned long. or explicitly masked out the top
bits when it only uses an int.

see my related rant at
https://lore.kernel.org/linux-api/Y1%2FDS6uoWP7OSkmd@arm.com/
Joey Gouly Nov. 8, 2022, 5:33 p.m. UTC | #6
Hi,

On Fri, Oct 28, 2022 at 06:03:18PM +0100, Mark Brown wrote:
> On Wed, Oct 26, 2022 at 04:04:57PM +0100, Joey Gouly wrote:
> 
> > Add some tests to cover the new PR_SET_MDWE prctl.
> 
> Some comments below but they're all stylistic and let's not make perfect
> be the enemy of the good here so
> 
> Reviewed-by: Mark Brown <broonie@kernel.org>

Thanks for the review, however I won't keep your R-b tag because I'm going to
move forward with Kees' approach from:

https://lore.kernel.org/linux-arm-kernel/202210281314.C5D3414722@keescook/T/#m45ac9de6c205b560d072a65e4e67e2a7ee363588

Thanks to Kees for rewriting that.

> 
> and we can iterate later rather than blocking anything on the testcase.
> 
> > +#ifdef __aarch64__
> > +#define PROT_BTI      0x10            /* BTI guarded page */
> > +#endif
> 
> We should get this from the kernel headers shouldn't we?  We generally
> rely on things getting pulled in from there rather than locally
> defining.

I believe the mman.h included is from the toolchain, not the kernel's uapi headers.
The toolchain I was using didn't have PROT_BTI defined in its mman.h

> 
> > +#define TEST1 "mmap(PROT_WRITE | PROT_EXEC)\n"
> > +#define TEST2 "mmap(PROT_WRITE); mprotect(PROT_EXEC)\n"
> > +#define TEST3 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_READ)\n"
> > +#define TEST4 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_BTI)\n"
> 
> > +int test1(int mdwe_enabled)
> > +{
> 
> It feels like we could usefully make an array of
> 
> 	struct test {
> 		int (*run)(bool mdwe_enabled);
> 		char *name;
> 	}
> 
> then we'd need fewer ifdefs, things could be more usefully named and
> it'd be a bit easier to add new cases.
> 
> > +#ifdef __aarch64__
> > +	ksft_set_plan(12);
> > +#else
> > +	ksft_set_plan(9);
> > +#endif
> 
> That'd just be ksft_test_plan(3 * ARRAY_SIZE(tests).
> 
> > +	// First run the tests without MDWE
> > +	test_result(test1(0), TEST1);
> > +	test_result(test2(0), TEST2);
> > +	test_result(test3(0), TEST3);
> > +#ifdef __aarch64__
> > +	test_result(test4(0), TEST4);
> > +#endif
> 
> and these calls to the tests would all be iterating over the array.

These comments are solved by the kselftest_harness approach that Kees suggested.

Thanks,
Joey
Mark Brown Nov. 9, 2022, 1:33 p.m. UTC | #7
On Tue, Nov 08, 2022 at 05:33:03PM +0000, Joey Gouly wrote:
> On Fri, Oct 28, 2022 at 06:03:18PM +0100, Mark Brown wrote:
> > On Wed, Oct 26, 2022 at 04:04:57PM +0100, Joey Gouly wrote:

> > > +#ifdef __aarch64__
> > > +#define PROT_BTI      0x10            /* BTI guarded page */
> > > +#endif

> > We should get this from the kernel headers shouldn't we?  We generally
> > rely on things getting pulled in from there rather than locally
> > defining.

> I believe the mman.h included is from the toolchain, not the kernel's uapi headers.
> The toolchain I was using didn't have PROT_BTI defined in its mman.h

I'd expect that whatever we're doing in the build process ought to be
overriding the default headers provided by the toolchain, that's kind of
the point here...
diff mbox series

Patch

diff --git a/tools/testing/selftests/vm/mdwe_test.c b/tools/testing/selftests/vm/mdwe_test.c
new file mode 100644
index 000000000000..67f3fc06d069
--- /dev/null
+++ b/tools/testing/selftests/vm/mdwe_test.c
@@ -0,0 +1,194 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#include <asm/hwcap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <linux/prctl.h>
+
+#include "../kselftest.h"
+
+#define PR_SET_MDWE                     65
+# define PR_MDWE_FLAG_MMAP              1
+
+#define PR_GET_MDWE                     66
+
+#ifdef __aarch64__
+#define PROT_BTI      0x10            /* BTI guarded page */
+#endif
+
+#define TEST1 "mmap(PROT_WRITE | PROT_EXEC)\n"
+#define TEST2 "mmap(PROT_WRITE); mprotect(PROT_EXEC)\n"
+#define TEST3 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_READ)\n"
+#define TEST4 "mmap(PROT_EXEC); mprotect(PROT_EXEC | PROT_BTI)\n"
+
+int fork_test(int (*func)(int))
+{
+	pid_t pid;
+	int status;
+
+	pid = fork();
+	if (pid < 0) {
+		printf("fork failed\n");
+		return KSFT_FAIL;
+	}
+
+	if (pid == 0)
+		exit(func(1));
+
+	waitpid(pid, &status, 0);
+
+	if (WIFEXITED(status))
+		return WEXITSTATUS(status);
+
+	return 0;
+}
+
+static inline void test_result(int err, const char *msg)
+{
+	switch (err) {
+	case KSFT_PASS:
+		ksft_test_result_pass(msg);
+		break;
+	case KSFT_FAIL:
+		ksft_test_result_fail(msg);
+		break;
+	case KSFT_SKIP:
+		ksft_test_result_skip(msg);
+		break;
+	default:
+		ksft_test_result_error("Unknown return code %d from %s",
+				       err, msg);
+		break;
+	}
+}
+
+int test1(int mdwe_enabled)
+{
+	void *p;
+
+	int size = getpagesize();
+	int mmap_flags = MAP_SHARED | MAP_ANONYMOUS;
+
+	p = mmap(0, size, PROT_WRITE | PROT_EXEC, mmap_flags, 0, 0);
+
+	if (mdwe_enabled)
+		return p == MAP_FAILED ? KSFT_PASS : KSFT_FAIL;
+	else
+		return p != MAP_FAILED ? KSFT_PASS : KSFT_FAIL;
+}
+
+int test2(int mdwe_enabled)
+{
+	void *p;
+	int ret;
+
+	int size = getpagesize();
+	int mmap_flags = MAP_SHARED | MAP_ANONYMOUS;
+
+	p = mmap(0, size, PROT_WRITE, mmap_flags, 0, 0);
+	if (p == MAP_FAILED)
+		return 0;
+	ret = mprotect(p, size, PROT_EXEC);
+
+	if (mdwe_enabled)
+		return ret < 0 ? KSFT_PASS : KSFT_FAIL;
+	else
+		return ret == 0 ? KSFT_PASS : KSFT_FAIL;
+}
+
+int test3(int mdwe_enabled)
+{
+	void *p;
+	int ret;
+
+	int size = getpagesize();
+	int mmap_flags = MAP_SHARED | MAP_ANONYMOUS;
+
+	p = mmap(0, size, PROT_EXEC, mmap_flags, 0, 0);
+	if (p == MAP_FAILED)
+		return 0;
+
+	ret = mprotect(p, size, PROT_EXEC | PROT_READ);
+
+	return ret == 0 ? KSFT_PASS : KSFT_FAIL;
+}
+
+#ifdef __aarch64__
+int test4(int mdwe_enabled)
+{
+	void *p;
+	int ret;
+
+	int size = getpagesize();
+	int mmap_flags = MAP_SHARED | MAP_ANONYMOUS;
+
+	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
+		return KSFT_SKIP;
+
+	p = mmap(0, size, PROT_EXEC, mmap_flags, 0, 0);
+	if (p == MAP_FAILED)
+		return KSFT_FAIL;
+
+	ret = mprotect(p, size, PROT_EXEC | PROT_BTI);
+
+	return ret == 0 ? KSFT_PASS : KSFT_FAIL;
+}
+#endif
+
+int main(void)
+{
+	int ret;
+
+	ksft_print_header();
+#ifdef __aarch64__
+	ksft_set_plan(12);
+#else
+	ksft_set_plan(9);
+#endif
+
+	// First run the tests without MDWE
+	test_result(test1(0), TEST1);
+	test_result(test2(0), TEST2);
+	test_result(test3(0), TEST3);
+#ifdef __aarch64__
+	test_result(test4(0), TEST4);
+#endif
+
+	// Enable MDWE and then run the tests again.
+	ret = prctl(PR_SET_MDWE, PR_MDWE_FLAG_MMAP, 0, 0, 0);
+	if (ret < 0) {
+		ksft_print_msg("PR_SET_MDWE failed or unsupported!\n");
+		goto exit;
+	}
+
+	ret = prctl(PR_GET_MDWE, PR_MDWE_FLAG_MMAP, 0, 0, 0);
+	if (ret == 0)
+		ksft_exit_fail_msg("PR_GET_MDWE failed!");
+
+	test_result(test1(1), "MDWE: " TEST1);
+	test_result(test2(1), "MDWE: " TEST2);
+	test_result(test3(1), "MDWE: " TEST3);
+#ifdef __aarch64__
+	test_result(test4(1), "MDWE: " TEST4);
+#endif
+
+	// Verify the MDWE setting is transferred when fork()ing
+	test_result(fork_test(test1), "MDWE+fork: " TEST1);
+	test_result(fork_test(test2), "MDWE+fork: " TEST2);
+	test_result(fork_test(test3), "MDWE+fork: " TEST3);
+#ifdef __aarch64__
+	test_result(fork_test(test4), "MDWE+fork: " TEST4);
+#endif
+
+exit:
+	ksft_finished();
+
+	return 0;
+}
+