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
24TEST(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
48FIXTURE(consecutive_prctl_flags) {};
49FIXTURE_SETUP(consecutive_prctl_flags) {}
50FIXTURE_TEARDOWN(consecutive_prctl_flags) {}
51
52FIXTURE_VARIANT(consecutive_prctl_flags)
53{
54	unsigned long first_flags;
55	unsigned long second_flags;
56	bool should_work;
57};
58
59FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags)
60{
61	.first_flags = 0,
62	.second_flags = 0,
63	.should_work = true,
64};
65
66FIXTURE_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
73FIXTURE_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
80FIXTURE_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
87FIXTURE_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
94FIXTURE_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
101FIXTURE_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
108TEST_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
126FIXTURE(mdwe)
127{
128	void *p;
129	int flags;
130	size_t size;
131	pid_t pid;
132};
133
134FIXTURE_VARIANT(mdwe)
135{
136	bool enabled;
137	bool forked;
138	bool inherit;
139};
140
141FIXTURE_VARIANT_ADD(mdwe, stock)
142{
143	.enabled = false,
144	.forked = false,
145	.inherit = false,
146};
147
148FIXTURE_VARIANT_ADD(mdwe, enabled)
149{
150	.enabled = true,
151	.forked = false,
152	.inherit = true,
153};
154
155FIXTURE_VARIANT_ADD(mdwe, inherited)
156{
157	.enabled = true,
158	.forked = true,
159	.inherit = true,
160};
161
162FIXTURE_VARIANT_ADD(mdwe, not_inherited)
163{
164	.enabled = true,
165	.forked = true,
166	.inherit = false,
167};
168
169static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant)
170{
171	return variant->enabled && (!variant->forked || variant->inherit);
172}
173
174FIXTURE_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
212FIXTURE_TEARDOWN(mdwe)
213{
214	if (self->p && self->p != MAP_FAILED)
215		munmap(self->p, self->size);
216}
217
218TEST_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
224TEST_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
234TEST_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
245TEST_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
260TEST_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
275TEST_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
288TEST_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
304TEST_HARNESS_MAIN
305