diff mbox series

[2/2] Add mseal01 test

Message ID 20240610-mseal-v1-2-f659b9e97efa@suse.com
State Accepted
Headers show
Series Add mseal() testing suite | expand

Commit Message

Andrea Cervesato June 10, 2024, 9:44 a.m. UTC
From: Andrea Cervesato <andrea.cervesato@suse.com>

This is a smoke test that verifies if mseal() protects specific VMA
portions of a process. According to documentation, the syscall should
protect memory from the following actions:

- unmapping, moving to another location, and shrinking the size, via
  munmap() and mremap()
- moving or expanding a different VMA into the current location, via
  mremap()
- modifying a VMA via mmap(MAP_FIXED)
- mprotect() and pkey_mprotect()
- destructive madvice() behaviors (e.g. MADV_DONTNEED) for anonymous
  memory, when users don’t have write permission to the memory

Any of the described actions is recognized via EPERM errno.

Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
---
 testcases/kernel/syscalls/mseal/.gitignore |   1 +
 testcases/kernel/syscalls/mseal/Makefile   |   7 ++
 testcases/kernel/syscalls/mseal/mseal01.c  | 177 +++++++++++++++++++++++++++++
 3 files changed, 185 insertions(+)

Comments

Cyril Hrubis July 11, 2024, 3:53 p.m. UTC | #1
Hi!
> +#ifndef TST_ABI32

This is the worst type of check you can do and should be used only when
the test code does not compile at all.

Instead you should check the return value from mseal() syscall and TCONF
if you get ENOSYS on 32bit, at least the syscalls numbers seems to be
allocated so you should get ENOSYS at runtime.
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/mseal/.gitignore b/testcases/kernel/syscalls/mseal/.gitignore
new file mode 100644
index 000000000..e13090994
--- /dev/null
+++ b/testcases/kernel/syscalls/mseal/.gitignore
@@ -0,0 +1 @@ 
+mseal01
diff --git a/testcases/kernel/syscalls/mseal/Makefile b/testcases/kernel/syscalls/mseal/Makefile
new file mode 100644
index 000000000..35317f446
--- /dev/null
+++ b/testcases/kernel/syscalls/mseal/Makefile
@@ -0,0 +1,7 @@ 
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2023 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+
+top_srcdir		?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/mseal/mseal01.c b/testcases/kernel/syscalls/mseal/mseal01.c
new file mode 100644
index 000000000..bf5e71612
--- /dev/null
+++ b/testcases/kernel/syscalls/mseal/mseal01.c
@@ -0,0 +1,177 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * [Description]
+ *
+ * This is a smoke test that verifies if mseal() protects specific VMA portions
+ * of a process. According to documentation, the syscall should protect memory
+ * from the following actions:
+ *
+ * - unmapping, moving to another location, and shrinking the size, via munmap()
+ *   and mremap()
+ * - moving or expanding a different VMA into the current location, via mremap()
+ * - modifying a VMA via mmap(MAP_FIXED)
+ * - mprotect() and pkey_mprotect()
+ * - destructive madvice() behaviors (e.g. MADV_DONTNEED) for anonymous memory,
+ *   when users don’t have write permission to the memory
+ *
+ * Any of the described actions is recognized via EPERM errno.
+ */
+
+#define _GNU_SOURCE
+
+#include "tst_test.h"
+
+#ifndef TST_ABI32
+#include "lapi/syscalls.h"
+
+#define MEMPAGES 8
+#define MEMSEAL 2
+
+static void *mem_addr;
+static int mem_size;
+static int mem_offset;
+static int mem_alignment;
+
+static inline int sys_mseal(void *start, size_t len)
+{
+	return tst_syscall(__NR_mseal, start, len, 0);
+}
+
+static void test_mprotect(void)
+{
+	TST_EXP_FAIL(mprotect(mem_addr, mem_size, PROT_NONE), EPERM);
+}
+
+static void test_pkey_mprotect(void)
+{
+	int ret;
+	int pkey;
+
+	pkey = pkey_alloc(0, 0);
+	if (pkey == -1) {
+		if (errno == EINVAL)
+			tst_brk(TCONF, "pku is not supported on this CPU");
+
+		tst_brk(TBROK | TERRNO, "pkey_alloc() error");
+	}
+
+	TST_EXP_FAIL(pkey_mprotect(
+		mem_addr, mem_size,
+		PROT_NONE,
+		pkey),
+		EPERM);
+
+	ret = pkey_free(pkey);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "pkey_free() error");
+}
+
+static void test_madvise(void)
+{
+	TST_EXP_FAIL(madvise(mem_addr, mem_size, MADV_DONTNEED), EPERM);
+}
+
+static void test_munmap(void)
+{
+	TST_EXP_FAIL(munmap(mem_addr, mem_size), EPERM);
+}
+
+static void test_mremap_resize(void)
+{
+	void *new_addr;
+	size_t new_size = 2 * mem_alignment;
+
+	new_addr = SAFE_MMAP(NULL, mem_size,
+		PROT_READ,
+		MAP_ANONYMOUS | MAP_PRIVATE,
+		-1, 0);
+
+	TST_EXP_FAIL_PTR_VOID(mremap(mem_addr, mem_size, new_size,
+		MREMAP_MAYMOVE | MREMAP_FIXED,
+		new_addr),
+		EPERM);
+
+	SAFE_MUNMAP(new_addr, new_size);
+}
+
+static void test_mmap_change_prot(void)
+{
+	TST_EXP_FAIL_PTR_VOID(mmap(mem_addr, mem_size,
+		PROT_READ,
+		MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
+		-1, 0), EPERM);
+}
+
+static struct tcase {
+	void (*func_test)(void);
+	int prot;
+	char *message;
+} tcases[] = {
+	{test_mprotect, PROT_READ | PROT_WRITE, "mprotect() availability"},
+	{test_pkey_mprotect, PROT_READ | PROT_WRITE, "pkey_mprotect() availability"},
+	{test_madvise, PROT_READ, "madvise() availability"},
+	{test_munmap, PROT_READ | PROT_WRITE, "munmap() availability from child"},
+	{test_mremap_resize, PROT_READ | PROT_WRITE, "mremap() address move/resize"},
+	{test_mmap_change_prot, PROT_READ | PROT_WRITE, "mmap() protection change"},
+};
+
+static void child(unsigned int n)
+{
+	struct tcase *tc = &tcases[n];
+
+	mem_addr = SAFE_MMAP(NULL, mem_size,
+		tc->prot,
+		MAP_ANONYMOUS | MAP_PRIVATE,
+		-1, 0);
+
+	tst_res(TINFO, "Testing %s", tc->message);
+
+	TST_EXP_PASS(sys_mseal(mem_addr + mem_offset, mem_alignment));
+
+	tc->func_test();
+}
+
+static void run(unsigned int n)
+{
+	/* the reason why we spawn a child is that mseal() will
+	 * protect VMA until process will call _exit()
+	 */
+	if (!SAFE_FORK()) {
+		child(n);
+		_exit(0);
+	}
+
+	tst_reap_children();
+
+	if (mem_addr != MAP_FAILED)
+		SAFE_MUNMAP(mem_addr, mem_size);
+}
+
+static void setup(void)
+{
+	mem_alignment = getpagesize();
+	mem_size = mem_alignment * MEMPAGES;
+	mem_offset = mem_alignment * MEMSEAL;
+}
+
+static void cleanup(void)
+{
+	if (mem_addr != MAP_FAILED)
+		SAFE_MUNMAP(mem_addr, mem_size);
+}
+
+static struct tst_test test = {
+	.test = run,
+	.tcnt = ARRAY_SIZE(tcases),
+	.setup = setup,
+	.cleanup = cleanup,
+	.min_kver = "6.10",
+	.forks_child = 1,
+};
+#else
+TST_TEST_TCONF("mseal() doesn't support 32bit arch");
+#endif