File Index
Files and directories
Sign in
FILE

mdwe_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/mm/mdwe_test.c C
  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
 23TEST(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
 47FIXTURE(consecutive_prctl_flags) {};
 48FIXTURE_SETUP(consecutive_prctl_flags) {}
 49FIXTURE_TEARDOWN(consecutive_prctl_flags) {}
 50
 51FIXTURE_VARIANT(consecutive_prctl_flags)
 52{
 53	unsigned long first_flags;
 54	unsigned long second_flags;
 55	bool should_work;
 56};
 57
 58FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags)
 59{
 60	.first_flags = 0,
 61	.second_flags = 0,
 62	.should_work = true,
 63};
 64
 65FIXTURE_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
 72FIXTURE_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
 79FIXTURE_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
 86FIXTURE_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
 93FIXTURE_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
100FIXTURE_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
107TEST_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
125FIXTURE(mdwe)
126{
127	void *p;
128	int flags;
129	size_t size;
130	pid_t pid;
131};
132
133FIXTURE_VARIANT(mdwe)
134{
135	bool enabled;
136	bool forked;
137	bool inherit;
138};
139
140FIXTURE_VARIANT_ADD(mdwe, stock)
141{
142	.enabled = false,
143	.forked = false,
144	.inherit = false,
145};
146
147FIXTURE_VARIANT_ADD(mdwe, enabled)
148{
149	.enabled = true,
150	.forked = false,
151	.inherit = true,
152};
153
154FIXTURE_VARIANT_ADD(mdwe, inherited)
155{
156	.enabled = true,
157	.forked = true,
158	.inherit = true,
159};
160
161FIXTURE_VARIANT_ADD(mdwe, not_inherited)
162{
163	.enabled = true,
164	.forked = true,
165	.inherit = false,
166};
167
168static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant)
169{
170	return variant->enabled && (!variant->forked || variant->inherit);
171}
172
173FIXTURE_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
211FIXTURE_TEARDOWN(mdwe)
212{
213	if (self->p && self->p != MAP_FAILED)
214		munmap(self->p, self->size);
215}
216
217TEST_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
223TEST_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
233TEST_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
244TEST_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
259TEST_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
274TEST_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
287TEST_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
303TEST_HARNESS_MAIN
~/ Read-only file preview.