• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
4  */
5 
6 /*\
7  * [Description]
8  *
9  * This is a smoke test that verifies if mseal() protects specific VMA portions
10  * of a process. According to documentation, the syscall should protect memory
11  * from the following actions:
12  *
13  * - unmapping, moving to another location, and shrinking the size, via munmap()
14  *   and mremap()
15  * - moving or expanding a different VMA into the current location, via mremap()
16  * - modifying a VMA via mmap(MAP_FIXED)
17  * - mprotect() and pkey_mprotect()
18  * - destructive madvice() behaviors (e.g. MADV_DONTNEED) for anonymous memory,
19  *   when users don't have write permission to the memory
20  *
21  * Any of the described actions is recognized via EPERM errno.
22  *
23  * TODO: support both raw syscall and libc wrapper via .test_variants.
24  */
25 
26 #define _GNU_SOURCE
27 
28 #include "tst_test.h"
29 #include "lapi/syscalls.h"
30 #include "lapi/pkey.h"
31 
32 #define MEMPAGES 8
33 #define MEMSEAL 2
34 
35 static void *mem_addr;
36 static int mem_size;
37 static int mem_offset;
38 static int mem_alignment;
39 
sys_mseal(void * start,size_t len)40 static inline int sys_mseal(void *start, size_t len)
41 {
42 	return tst_syscall(__NR_mseal, start, len, 0);
43 }
44 
test_mprotect(void)45 static void test_mprotect(void)
46 {
47 	TST_EXP_FAIL(mprotect(mem_addr, mem_size, PROT_NONE), EPERM);
48 }
49 
test_pkey_mprotect(void)50 static void test_pkey_mprotect(void)
51 {
52 	int pkey;
53 
54 	check_pkey_support();
55 
56 	pkey = pkey_alloc(0, 0);
57 	if (pkey == -1)
58 		tst_brk(TBROK | TERRNO, "pkey_alloc failed");
59 
60 	TST_EXP_FAIL(pkey_mprotect(
61 		mem_addr, mem_size,
62 		PROT_NONE,
63 		pkey),
64 		EPERM);
65 
66 	if (pkey_free(pkey) == -1)
67 		tst_brk(TBROK | TERRNO, "pkey_free() error");
68 }
69 
test_madvise(void)70 static void test_madvise(void)
71 {
72 	TST_EXP_FAIL(madvise(mem_addr, mem_size, MADV_DONTNEED), EPERM);
73 }
74 
test_munmap(void)75 static void test_munmap(void)
76 {
77 	TST_EXP_FAIL(munmap(mem_addr, mem_size), EPERM);
78 }
79 
test_mremap_resize(void)80 static void test_mremap_resize(void)
81 {
82 	void *new_addr;
83 	size_t new_size = 2 * mem_alignment;
84 
85 	new_addr = SAFE_MMAP(NULL, mem_size,
86 		PROT_READ,
87 		MAP_ANONYMOUS | MAP_PRIVATE,
88 		-1, 0);
89 
90 	TST_EXP_FAIL_PTR_VOID(mremap(mem_addr, mem_size, new_size,
91 		MREMAP_MAYMOVE | MREMAP_FIXED,
92 		new_addr),
93 		EPERM);
94 
95 	SAFE_MUNMAP(new_addr, new_size);
96 }
97 
test_mmap_change_prot(void)98 static void test_mmap_change_prot(void)
99 {
100 	TST_EXP_FAIL_PTR_VOID(mmap(mem_addr, mem_size,
101 		PROT_READ,
102 		MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
103 		-1, 0), EPERM);
104 }
105 
106 static struct tcase {
107 	void (*func_test)(void);
108 	int prot;
109 	char *message;
110 } tcases[] = {
111 	{test_mprotect, PROT_READ | PROT_WRITE, "mprotect() availability"},
112 	{test_pkey_mprotect, PROT_READ | PROT_WRITE, "pkey_mprotect() availability"},
113 	{test_madvise, PROT_READ, "madvise() availability"},
114 	{test_munmap, PROT_READ | PROT_WRITE, "munmap() availability from child"},
115 	{test_mremap_resize, PROT_READ | PROT_WRITE, "mremap() address move/resize"},
116 	{test_mmap_change_prot, PROT_READ | PROT_WRITE, "mmap() protection change"},
117 };
118 
run(unsigned int n)119 static void run(unsigned int n)
120 {
121 	/* the reason why we spawn a child is that mseal() will
122 	 * protect VMA until process will call _exit()
123 	 */
124 	if (!SAFE_FORK()) {
125 		struct tcase *tc = &tcases[n];
126 
127 		mem_addr = SAFE_MMAP(NULL, mem_size,
128 			tc->prot,
129 			MAP_ANONYMOUS | MAP_PRIVATE,
130 			-1, 0);
131 
132 		tst_res(TINFO, "Testing %s", tc->message);
133 
134 		TST_EXP_PASS(sys_mseal(mem_addr + mem_offset, mem_alignment));
135 
136 		tc->func_test();
137 		_exit(0);
138 	}
139 }
140 
setup(void)141 static void setup(void)
142 {
143 	mem_alignment = getpagesize();
144 	mem_size = mem_alignment * MEMPAGES;
145 	mem_offset = mem_alignment * MEMSEAL;
146 }
147 
148 static struct tst_test test = {
149 	.test = run,
150 	.tcnt = ARRAY_SIZE(tcases),
151 	.setup = setup,
152 	.forks_child = 1,
153 };
154 
155