Commit | Line | Data |
---|---|---|
4cf1fe34 KC |
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 | ||
80921623 | 10 | #define _GNU_SOURCE |
4cf1fe34 KC |
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 | { | |
2dc539ac FR |
26 | EXPECT_LT(prctl(PR_SET_MDWE, PR_MDWE_NO_INHERIT, 0L, 0L, 7L), 0); |
27 | EXPECT_EQ(errno, EINVAL); | |
28 | ||
4cf1fe34 | 29 | EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0); |
c93d05a7 | 30 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 31 | EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0); |
c93d05a7 | 32 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 33 | EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0); |
c93d05a7 | 34 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 35 | EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0); |
c93d05a7 | 36 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 KC |
37 | |
38 | EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0); | |
c93d05a7 | 39 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 40 | EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0); |
c93d05a7 | 41 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 42 | EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0); |
c93d05a7 | 43 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 | 44 | EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0); |
c93d05a7 | 45 | EXPECT_EQ(errno, EINVAL); |
4cf1fe34 KC |
46 | } |
47 | ||
2dc539ac FR |
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 | ||
4cf1fe34 KC |
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; | |
2dc539ac | 138 | bool inherit; |
4cf1fe34 KC |
139 | }; |
140 | ||
141 | FIXTURE_VARIANT_ADD(mdwe, stock) | |
142 | { | |
29d68b21 | 143 | .enabled = false, |
4cf1fe34 | 144 | .forked = false, |
2dc539ac | 145 | .inherit = false, |
4cf1fe34 KC |
146 | }; |
147 | ||
148 | FIXTURE_VARIANT_ADD(mdwe, enabled) | |
149 | { | |
29d68b21 | 150 | .enabled = true, |
4cf1fe34 | 151 | .forked = false, |
2dc539ac | 152 | .inherit = true, |
4cf1fe34 KC |
153 | }; |
154 | ||
2dc539ac | 155 | FIXTURE_VARIANT_ADD(mdwe, inherited) |
4cf1fe34 | 156 | { |
29d68b21 | 157 | .enabled = true, |
4cf1fe34 | 158 | .forked = true, |
2dc539ac | 159 | .inherit = true, |
4cf1fe34 KC |
160 | }; |
161 | ||
2dc539ac FR |
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 | ||
4cf1fe34 KC |
174 | FIXTURE_SETUP(mdwe) |
175 | { | |
2dc539ac | 176 | unsigned long mdwe_flags; |
4cf1fe34 KC |
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 | ||
2dc539ac FR |
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); | |
4cf1fe34 KC |
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); | |
2dc539ac | 196 | ASSERT_EQ(ret, mdwe_flags); |
4cf1fe34 KC |
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); | |
2dc539ac | 227 | if (executable_map_should_fail(variant)) { |
4cf1fe34 KC |
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); | |
2dc539ac | 253 | if (executable_map_should_fail(variant)) { |
4cf1fe34 KC |
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); | |
2dc539ac | 268 | if (executable_map_should_fail(variant)) { |
4cf1fe34 KC |
269 | EXPECT_LT(ret, 0); |
270 | } else { | |
271 | EXPECT_EQ(ret, 0); | |
272 | } | |
273 | } | |
274 | ||
275 | TEST_F(mdwe, mmap_FIXED) | |
276 | { | |
d035230e | 277 | void *p; |
4cf1fe34 | 278 | |
4cf1fe34 KC |
279 | self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0); |
280 | ASSERT_NE(self->p, MAP_FAILED); | |
281 | ||
a27e2e2d FR |
282 | /* MAP_FIXED unmaps the existing page before mapping which is allowed */ |
283 | p = mmap(self->p, self->size, PROT_READ | PROT_EXEC, | |
4cf1fe34 | 284 | self->flags | MAP_FIXED, 0, 0); |
a27e2e2d | 285 | EXPECT_EQ(p, self->p); |
4cf1fe34 KC |
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 |