xref: /linux/tools/testing/selftests/mm/mdwe_test.c (revision 8f8d74ee110c02137f5b78ca0a2bd6c10331f267)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #ifdef __aarch64__
4 #include <asm/hwcap.h>
5 #endif
6 
7 #include <linux/mman.h>
8 #include <linux/prctl.h>
9 
10 #define _GNU_SOURCE
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/auxv.h>
14 #include <sys/prctl.h>
15 #include <sys/wait.h>
16 #include <unistd.h>
17 
18 #include "../kselftest_harness.h"
19 
20 #ifndef __aarch64__
21 # define PROT_BTI	0
22 #endif
23 
24 TEST(prctl_flags)
25 {
26 	EXPECT_LT(prctl(PR_SET_MDWE, PR_MDWE_NO_INHERIT, 0L, 0L, 7L), 0);
27 	EXPECT_EQ(errno, EINVAL);
28 
29 	EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
30 	EXPECT_EQ(errno, EINVAL);
31 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
32 	EXPECT_EQ(errno, EINVAL);
33 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
34 	EXPECT_EQ(errno, EINVAL);
35 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
36 	EXPECT_EQ(errno, EINVAL);
37 
38 	EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
39 	EXPECT_EQ(errno, EINVAL);
40 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
41 	EXPECT_EQ(errno, EINVAL);
42 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
43 	EXPECT_EQ(errno, EINVAL);
44 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
45 	EXPECT_EQ(errno, EINVAL);
46 }
47 
48 FIXTURE(consecutive_prctl_flags) {};
49 FIXTURE_SETUP(consecutive_prctl_flags) {}
50 FIXTURE_TEARDOWN(consecutive_prctl_flags) {}
51 
52 FIXTURE_VARIANT(consecutive_prctl_flags)
53 {
54 	unsigned long first_flags;
55 	unsigned long second_flags;
56 	bool should_work;
57 };
58 
59 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags)
60 {
61 	.first_flags = 0,
62 	.second_flags = 0,
63 	.should_work = true,
64 };
65 
66 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_exec_gain)
67 {
68 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
69 	.second_flags = PR_MDWE_REFUSE_EXEC_GAIN,
70 	.should_work = true,
71 };
72 
73 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_both_flags)
74 {
75 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
76 	.second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
77 	.should_work = true,
78 };
79 
80 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe)
81 {
82 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
83 	.second_flags = 0,
84 	.should_work = false,
85 };
86 
87 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe_no_inherit)
88 {
89 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
90 	.second_flags = 0,
91 	.should_work = false,
92 };
93 
94 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_no_inherit)
95 {
96 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
97 	.second_flags = PR_MDWE_REFUSE_EXEC_GAIN,
98 	.should_work = false,
99 };
100 
101 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_enable_no_inherit)
102 {
103 	.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
104 	.second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
105 	.should_work = false,
106 };
107 
108 TEST_F(consecutive_prctl_flags, two_prctls)
109 {
110 	int ret;
111 
112 	EXPECT_EQ(prctl(PR_SET_MDWE, variant->first_flags, 0L, 0L, 0L), 0);
113 
114 	ret = prctl(PR_SET_MDWE, variant->second_flags, 0L, 0L, 0L);
115 	if (variant->should_work) {
116 		EXPECT_EQ(ret, 0);
117 
118 		ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
119 		ASSERT_EQ(ret, variant->second_flags);
120 	} else {
121 		EXPECT_NE(ret, 0);
122 		ASSERT_EQ(errno, EPERM);
123 	}
124 }
125 
126 FIXTURE(mdwe)
127 {
128 	void *p;
129 	int flags;
130 	size_t size;
131 	pid_t pid;
132 };
133 
134 FIXTURE_VARIANT(mdwe)
135 {
136 	bool enabled;
137 	bool forked;
138 	bool inherit;
139 };
140 
141 FIXTURE_VARIANT_ADD(mdwe, stock)
142 {
143 	.enabled = false,
144 	.forked = false,
145 	.inherit = false,
146 };
147 
148 FIXTURE_VARIANT_ADD(mdwe, enabled)
149 {
150 	.enabled = true,
151 	.forked = false,
152 	.inherit = true,
153 };
154 
155 FIXTURE_VARIANT_ADD(mdwe, inherited)
156 {
157 	.enabled = true,
158 	.forked = true,
159 	.inherit = true,
160 };
161 
162 FIXTURE_VARIANT_ADD(mdwe, not_inherited)
163 {
164 	.enabled = true,
165 	.forked = true,
166 	.inherit = false,
167 };
168 
169 static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant)
170 {
171 	return variant->enabled && (!variant->forked || variant->inherit);
172 }
173 
174 FIXTURE_SETUP(mdwe)
175 {
176 	unsigned long mdwe_flags;
177 	int ret, status;
178 
179 	self->p = NULL;
180 	self->flags = MAP_SHARED | MAP_ANONYMOUS;
181 	self->size = getpagesize();
182 
183 	if (!variant->enabled)
184 		return;
185 
186 	mdwe_flags = PR_MDWE_REFUSE_EXEC_GAIN;
187 	if (!variant->inherit)
188 		mdwe_flags |= PR_MDWE_NO_INHERIT;
189 
190 	ret = prctl(PR_SET_MDWE, mdwe_flags, 0L, 0L, 0L);
191 	ASSERT_EQ(ret, 0) {
192 		TH_LOG("PR_SET_MDWE failed or unsupported");
193 	}
194 
195 	ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
196 	ASSERT_EQ(ret, mdwe_flags);
197 
198 	if (variant->forked) {
199 		self->pid = fork();
200 		ASSERT_GE(self->pid, 0) {
201 			TH_LOG("fork failed\n");
202 		}
203 
204 		if (self->pid > 0) {
205 			ret = waitpid(self->pid, &status, 0);
206 			ASSERT_TRUE(WIFEXITED(status));
207 			exit(WEXITSTATUS(status));
208 		}
209 	}
210 }
211 
212 FIXTURE_TEARDOWN(mdwe)
213 {
214 	if (self->p && self->p != MAP_FAILED)
215 		munmap(self->p, self->size);
216 }
217 
218 TEST_F(mdwe, mmap_READ_EXEC)
219 {
220 	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
221 	EXPECT_NE(self->p, MAP_FAILED);
222 }
223 
224 TEST_F(mdwe, mmap_WRITE_EXEC)
225 {
226 	self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
227 	if (executable_map_should_fail(variant)) {
228 		EXPECT_EQ(self->p, MAP_FAILED);
229 	} else {
230 		EXPECT_NE(self->p, MAP_FAILED);
231 	}
232 }
233 
234 TEST_F(mdwe, mprotect_stay_EXEC)
235 {
236 	int ret;
237 
238 	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
239 	ASSERT_NE(self->p, MAP_FAILED);
240 
241 	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
242 	EXPECT_EQ(ret, 0);
243 }
244 
245 TEST_F(mdwe, mprotect_add_EXEC)
246 {
247 	int ret;
248 
249 	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
250 	ASSERT_NE(self->p, MAP_FAILED);
251 
252 	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
253 	if (executable_map_should_fail(variant)) {
254 		EXPECT_LT(ret, 0);
255 	} else {
256 		EXPECT_EQ(ret, 0);
257 	}
258 }
259 
260 TEST_F(mdwe, mprotect_WRITE_EXEC)
261 {
262 	int ret;
263 
264 	self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
265 	ASSERT_NE(self->p, MAP_FAILED);
266 
267 	ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
268 	if (executable_map_should_fail(variant)) {
269 		EXPECT_LT(ret, 0);
270 	} else {
271 		EXPECT_EQ(ret, 0);
272 	}
273 }
274 
275 TEST_F(mdwe, mmap_FIXED)
276 {
277 	void *p;
278 
279 	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
280 	ASSERT_NE(self->p, MAP_FAILED);
281 
282 	/* MAP_FIXED unmaps the existing page before mapping which is allowed */
283 	p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
284 		 self->flags | MAP_FIXED, 0, 0);
285 	EXPECT_EQ(p, self->p);
286 }
287 
288 TEST_F(mdwe, arm64_BTI)
289 {
290 	int ret;
291 
292 #ifdef __aarch64__
293 	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
294 #endif
295 		SKIP(return, "HWCAP2_BTI not supported");
296 
297 	self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
298 	ASSERT_NE(self->p, MAP_FAILED);
299 
300 	ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
301 	EXPECT_EQ(ret, 0);
302 }
303 
304 TEST_HARNESS_MAIN
305