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