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
TEST(prctl_flags)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
FIXTURE(consecutive_prctl_flags)47 FIXTURE(consecutive_prctl_flags) {};
FIXTURE_SETUP(consecutive_prctl_flags)48 FIXTURE_SETUP(consecutive_prctl_flags) {}
FIXTURE_TEARDOWN(consecutive_prctl_flags)49 FIXTURE_TEARDOWN(consecutive_prctl_flags) {}
50
FIXTURE_VARIANT(consecutive_prctl_flags)51 FIXTURE_VARIANT(consecutive_prctl_flags)
52 {
53 unsigned long first_flags;
54 unsigned long second_flags;
55 bool should_work;
56 };
57
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,can_keep_no_flags)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,can_keep_exec_gain)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,can_keep_both_flags)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,cant_disable_mdwe)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,cant_disable_mdwe_no_inherit)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,cant_disable_no_inherit)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
FIXTURE_VARIANT_ADD(consecutive_prctl_flags,cant_enable_no_inherit)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
TEST_F(consecutive_prctl_flags,two_prctls)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
FIXTURE(mdwe)125 FIXTURE(mdwe)
126 {
127 void *p;
128 int flags;
129 size_t size;
130 pid_t pid;
131 };
132
FIXTURE_VARIANT(mdwe)133 FIXTURE_VARIANT(mdwe)
134 {
135 bool enabled;
136 bool forked;
137 bool inherit;
138 };
139
FIXTURE_VARIANT_ADD(mdwe,stock)140 FIXTURE_VARIANT_ADD(mdwe, stock)
141 {
142 .enabled = false,
143 .forked = false,
144 .inherit = false,
145 };
146
FIXTURE_VARIANT_ADD(mdwe,enabled)147 FIXTURE_VARIANT_ADD(mdwe, enabled)
148 {
149 .enabled = true,
150 .forked = false,
151 .inherit = true,
152 };
153
FIXTURE_VARIANT_ADD(mdwe,inherited)154 FIXTURE_VARIANT_ADD(mdwe, inherited)
155 {
156 .enabled = true,
157 .forked = true,
158 .inherit = true,
159 };
160
FIXTURE_VARIANT_ADD(mdwe,not_inherited)161 FIXTURE_VARIANT_ADD(mdwe, not_inherited)
162 {
163 .enabled = true,
164 .forked = true,
165 .inherit = false,
166 };
167
executable_map_should_fail(const FIXTURE_VARIANT (mdwe)* variant)168 static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant)
169 {
170 return variant->enabled && (!variant->forked || variant->inherit);
171 }
172
FIXTURE_SETUP(mdwe)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
FIXTURE_TEARDOWN(mdwe)211 FIXTURE_TEARDOWN(mdwe)
212 {
213 if (self->p && self->p != MAP_FAILED)
214 munmap(self->p, self->size);
215 }
216
TEST_F(mdwe,mmap_READ_EXEC)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
TEST_F(mdwe,mmap_WRITE_EXEC)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
TEST_F(mdwe,mprotect_stay_EXEC)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
TEST_F(mdwe,mprotect_add_EXEC)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
TEST_F(mdwe,mprotect_WRITE_EXEC)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
TEST_F(mdwe,mmap_FIXED)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
TEST_F(mdwe,arm64_BTI)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