1 /*
2 * Copyright (c) 2024 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <arm_acle.h>
30 #include <darwintest.h>
31 #include <libproc.h>
32 #include <mach/mach.h>
33 #include <mach/mach_vm.h>
34 #include <signal.h>
35 #include <spawn_private.h>
36 #include <stdbool.h>
37 #include <stdlib.h>
38 #include <sys/spawn_internal.h>
39 #include <sys/sysctl.h>
40
41 #include "arm_mte_utilities.h"
42
43 void
kill_child(int child_pid)44 kill_child(int child_pid)
45 {
46 T_ASSERT_POSIX_ZERO(kill(child_pid, SIGKILL), "kill(child_pid, SIGKILL)");
47 T_ASSERT_NE(signal(SIGALRM, SIG_DFL), SIG_ERR, NULL);
48 T_ASSERT_POSIX_SUCCESS(alarm(1), NULL);
49 int status = 0;
50 T_ASSERT_POSIX_SUCCESS(waitpid(child_pid, &status, 0), "waitpid(child_pid)");
51 // Ensure the child was killed
52 T_ASSERT_TRUE(WIFSIGNALED(status), "exited due to signal");
53 T_ASSERT_EQ(WTERMSIG(status), SIGKILL, "killed with SIGKILL");
54 }
55
56 bool
validate_proc_pidinfo_mte_status(int child_pid,bool expect_mte_enabled)57 validate_proc_pidinfo_mte_status(int child_pid,
58 bool expect_mte_enabled)
59 {
60 // Collect process info via PROC_PIDTBSDINFO
61 struct proc_bsdinfo bsd_info;
62 int ret =
63 proc_pidinfo(child_pid, PROC_PIDTBSDINFO, 0, &bsd_info, sizeof(bsd_info));
64 T_QUIET; T_ASSERT_EQ((unsigned long)ret, sizeof(bsd_info), "PROC_PIDTBSDINFO");
65
66 // Collect process info via PROC_PIDT_SHORTBSDINFO
67 struct proc_bsdshortinfo bsd_short_info;
68 ret = proc_pidinfo(child_pid, PROC_PIDT_SHORTBSDINFO, 0, &bsd_short_info,
69 sizeof(bsd_short_info));
70 T_QUIET; T_ASSERT_EQ((unsigned long)ret, sizeof(bsd_short_info),
71 "PROC_PIDT_SHORTBSDINFO");
72
73 // Finally, ensure both mechanisms report the expected MTE status flag
74 if (expect_mte_enabled) {
75 T_QUIET; T_EXPECT_BITS_SET(bsd_info.pbi_flags, PROC_FLAG_SEC_ENABLED,
76 "Expect pbi_flags & PROC_FLAG_SEC_ENABLED != 0");
77 T_QUIET; T_EXPECT_BITS_SET(bsd_short_info.pbsi_flags, PROC_FLAG_SEC_ENABLED,
78 "Expect pbi_flags & PROC_FLAG_SEC_ENABLED != 0");
79
80 return (bsd_short_info.pbsi_flags & PROC_FLAG_SEC_ENABLED) && (bsd_info.pbi_flags & PROC_FLAG_SEC_ENABLED);
81 } else {
82 T_QUIET; T_EXPECT_BITS_NOTSET(bsd_info.pbi_flags, PROC_FLAG_SEC_ENABLED,
83 "Expect pbi_flags & PROC_FLAG_SEC_ENABLED == 0");
84 T_QUIET; T_EXPECT_BITS_NOTSET(bsd_short_info.pbsi_flags, PROC_FLAG_SEC_ENABLED,
85 "Expect pbi_flags & PROC_FLAG_SEC_ENABLED == 0");
86
87 return (bsd_info.pbi_flags & PROC_FLAG_SEC_ENABLED) == 0 && (bsd_info.pbi_flags & PROC_FLAG_SEC_ENABLED) == 0;
88 }
89 }
90
91 bool
validate_proc_pidinfo_mte_soft_mode_status(int child_pid,bool expect_mte_soft_mode_enabled)92 validate_proc_pidinfo_mte_soft_mode_status(int child_pid,
93 bool expect_mte_soft_mode_enabled)
94 {
95 // Collect process info via PROC_PIDTBSDINFO
96 struct proc_bsdinfo bsd_info;
97 int ret =
98 proc_pidinfo(child_pid, PROC_PIDTBSDINFO, 0, &bsd_info, sizeof(bsd_info));
99 T_QUIET; T_ASSERT_EQ((unsigned long)ret, sizeof(bsd_info), "PROC_PIDTBSDINFO");
100
101 // Collect process info via PROC_PIDT_SHORTBSDINFO
102 struct proc_bsdshortinfo bsd_short_info;
103 ret = proc_pidinfo(child_pid, PROC_PIDT_SHORTBSDINFO, 0, &bsd_short_info,
104 sizeof(bsd_short_info));
105 T_QUIET; T_ASSERT_EQ((unsigned long)ret, sizeof(bsd_short_info),
106 "PROC_PIDT_SHORTBSDINFO");
107
108 // Finally, ensure both mechanisms report the expected MTE status flag
109 if (expect_mte_soft_mode_enabled) {
110 T_QUIET; T_EXPECT_BITS_SET(bsd_info.pbi_flags, PROC_FLAG_SEC_BYPASS_ENABLED,
111 "Expect pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED != 0");
112 T_QUIET; T_EXPECT_BITS_SET(bsd_short_info.pbsi_flags, PROC_FLAG_SEC_BYPASS_ENABLED,
113 "Expect pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED != 0");
114
115 return (bsd_short_info.pbsi_flags & PROC_FLAG_SEC_BYPASS_ENABLED) && (bsd_info.pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED);
116 } else {
117 T_QUIET; T_EXPECT_BITS_NOTSET(bsd_info.pbi_flags, PROC_FLAG_SEC_BYPASS_ENABLED,
118 "Expect pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED == 0");
119 T_QUIET; T_EXPECT_BITS_NOTSET(bsd_short_info.pbsi_flags, PROC_FLAG_SEC_BYPASS_ENABLED,
120 "Expect pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED == 0");
121
122 return (bsd_info.pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED) == 0 && (bsd_info.pbi_flags & PROC_FLAG_SEC_BYPASS_ENABLED) == 0;
123 }
124 }
125
126 bool
wait_for_child(int pid)127 wait_for_child(int pid)
128 {
129 int status;
130 if (waitpid(pid, &status, 0) == -1) {
131 T_LOG(
132 "wait_for_child: pid {%d} failed with WEXITSTATUS "
133 "of %d and status %d\n",
134 pid, WEXITSTATUS(status), status);
135 } else if (WIFEXITED(status) && !WEXITSTATUS(status)) {
136 // The program terminated normally and executed successfully from the
137 // child process
138 return true;
139 }
140 return false;
141 }
142
143 bool
fork_and_exec_new_process(char * new_argv[])144 fork_and_exec_new_process(char *new_argv[])
145 {
146 pid_t pid = fork();
147
148 if (pid == 0) { /* child process */
149 execv(new_argv[0], new_argv);
150 exit(127);
151 } else { /* parent waits for child to exit */
152 bool child_succeeded = wait_for_child(pid);
153 return child_succeeded;
154 }
155 }
156
157 /*
158 * posix_spawn_then_perform_actions_from_process() will execute the
159 * downstream defined process based on the setup rules and, if requested,
160 * applying the desired flags.
161 */
162 bool
posix_spawn_then_perform_action_from_process(char * new_argv[],uint8_t setup,uint16_t sec_flags)163 posix_spawn_then_perform_action_from_process(char *new_argv[], uint8_t setup,
164 uint16_t sec_flags)
165 {
166 pid_t child_pid = 0;
167 posix_spawnattr_t attr = NULL;
168 errno_t ret = posix_spawnattr_init(&attr);
169 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init");
170
171 switch (setup) {
172 case MTE_SPAWN_USE_VANILLA:
173 /* No further configuration for posix spawn expected */
174 break;
175 case MTE_SPAWN_USE_LEGACY_API:
176 ret = posix_spawnattr_set_use_sec_transition_shims_np(&attr, sec_flags);
177 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_set_use_sec_transition_shims_np");
178 break;
179 default:
180 T_FAIL("Unexpected setup op for posix_spawn_then_perform_action_from_process()");
181 return false;
182 }
183
184 ret = posix_spawn(&child_pid, new_argv[0], NULL, &attr, new_argv, NULL);
185 T_ASSERT_POSIX_ZERO(ret, "posix_spawn(%s)", new_argv[0]);
186 T_ASSERT_NE(child_pid, 0, "posix_spawn(%s)", new_argv[0]);
187
188 bool child_succeeded = wait_for_child(child_pid);
189
190 // Cleanup
191 ret = posix_spawnattr_destroy(&attr);
192 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_destroy");
193
194 // This is the return code of our primary executable
195 // We need to return 0 to indicate everything went smoothly
196 return child_succeeded;
197 }
198
199 int64_t
run_sysctl_test(const char * t,int64_t value)200 run_sysctl_test(const char *t, int64_t value)
201 {
202 char name[1024];
203 int64_t result = 0;
204 size_t s = sizeof(value);
205 int rc;
206
207 snprintf(name, sizeof(name), "debug.test.%s", t);
208 rc = sysctlbyname(name, &result, &s, &value, s);
209 T_ASSERT_POSIX_SUCCESS(rc, "sysctlbyname(%s)", t);
210 return result;
211 }
212
213 /*
214 * note: arm_mte_utilities.h defines an expect_signal macro which automatically
215 * stringifies signal instead of taking an explicit signal_name
216 */
217 void
218 expect_signal_impl(int signal, char *signal_name, void (^fn)(void), const char *msg)
219 {
220 pid_t pid = fork();
221 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
222
223 if (pid == 0) {
224 fn();
225 T_FAIL("%s: did not receive %s", msg, signal_name);
226 exit(1);
227 } else {
228 int status = 0;
229 T_QUIET; T_ASSERT_POSIX_SUCCESS(waitpid(pid, &status, 0), "waitpid");
230 T_EXPECT_TRUE(WIFSIGNALED(status), "%s: exited with signal", msg);
231 T_EXPECT_EQ(WTERMSIG(status), signal, "%s: exited with %s", msg, signal_name);
232 }
233 }
234
235 void
236 expect_sigkill(void (^fn)(void), const char *msg)
237 {
238 expect_signal(SIGKILL, fn, msg);
239 }
240
241 void
242 expect_normal_exit(void (^fn)(void), const char *msg)
243 {
244 pid_t pid = fork();
245 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
246
247 if (pid == 0) {
248 fn();
249 T_END;
250 } else {
251 int status = 0;
252 T_QUIET; T_ASSERT_POSIX_SUCCESS(waitpid(pid, &status, 0), "waitpid");
253 if (WIFSIGNALED(status)) {
254 T_FAIL("%s: exited with signal %d", msg, WTERMSIG(status));
255 } else {
256 T_PASS("%s: exited normally", msg);
257 T_EXPECT_EQ(WEXITSTATUS(status), 0, "%s: exited with status 0", msg);
258 }
259 }
260 }
261
262 void
263 assert_normal_exit(void (^fn)(void), const char *msg)
264 {
265 pid_t pid = fork();
266 T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
267
268 if (pid == 0) {
269 fn();
270 T_END;
271 } else {
272 int status = 0;
273 T_QUIET; T_ASSERT_POSIX_SUCCESS(waitpid(pid, &status, 0), "waitpid");
274 if (WIFSIGNALED(status)) {
275 T_ASSERT_FAIL("%s: exited with signal %d", msg, WTERMSIG(status));
276 } else {
277 T_PASS("%s: exited normally", msg);
278 T_ASSERT_EQ(WEXITSTATUS(status), 0, "%s: exited with status 0", msg);
279 }
280 }
281 }
282
283 void *
allocate_tagged_memory(mach_vm_size_t size,uint64_t * mask)284 allocate_tagged_memory(
285 mach_vm_size_t size,
286 uint64_t *mask)
287 {
288 mach_vm_address_t addr = 0;
289 T_LOG("Allocate tagged memory");
290 kern_return_t kr = mach_vm_allocate(
291 mach_task_self(),
292 &addr,
293 size,
294 VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
295 T_ASSERT_MACH_SUCCESS(kr, "Allocated tagged page");
296 T_QUIET; T_ASSERT_NE_ULLONG(0ULL, addr, "Allocated address is not null");
297
298 void *untagged_ptr = (void *)addr;
299
300 void *orig_tagged_ptr = __arm_mte_get_tag(untagged_ptr);
301 unsigned int orig_tag = extract_mte_tag(orig_tagged_ptr);
302 T_QUIET; T_ASSERT_EQ_UINT(orig_tag, 0U,
303 "Originally assigned tag is zero, tag: %u", orig_tag);
304
305 if (mask) {
306 uint64_t local_mask = __arm_mte_exclude_tag(orig_tagged_ptr, 0);
307 T_QUIET; T_EXPECT_EQ_LLONG(local_mask, (1LL << 0), "Zero tag is excluded");
308 *mask = local_mask;
309
310 /* Generate random tag */
311 void *tagged_ptr = NULL;
312 tagged_ptr = __arm_mte_create_random_tag(untagged_ptr, local_mask);
313 T_QUIET; T_EXPECT_NE_PTR(orig_tagged_ptr, tagged_ptr,
314 "Random tag was not taken from excluded tag set");
315 }
316 return orig_tagged_ptr;
317 }
318
319 /*
320 * tag: the tag value to tag the entire range with, or ~x to generate a random
321 * tag excluding x as a valid value
322 */
323 vm_address_t
allocate_and_tag_range(mach_vm_size_t size,uintptr_t tag)324 allocate_and_tag_range(mach_vm_size_t size, uintptr_t tag)
325 {
326 T_SETUPBEGIN;
327 T_QUIET; T_ASSERT_EQ(size % MTE_GRANULE_SIZE, 0ULL, "can't tag part of an MTE granule");
328 T_QUIET; T_ASSERT_TRUE((tag & ~0xFUL) == 0UL || (~tag & ~0xFUL) == 0UL, "tag must fit in four bits");
329
330 void *untagged_ptr = allocate_tagged_memory(size, NULL);
331 uint8_t *tagged_ptr;
332
333 if ((tag & 0xFUL) != tag) {
334 uintptr_t excluded = (uintptr_t) untagged_ptr | (~tag << MTE_TAG_SHIFT);
335 uint64_t mask = __arm_mte_exclude_tag((void*)excluded, ~tag);
336 tagged_ptr = __arm_mte_create_random_tag(untagged_ptr, mask);
337 } else {
338 tagged_ptr = (uint8_t*)((uintptr_t) untagged_ptr | (tag << MTE_TAG_SHIFT));
339 }
340
341 for (mach_vm_size_t offset = 0; offset < size; offset += MTE_GRANULE_SIZE) {
342 __arm_mte_set_tag(&tagged_ptr[offset]);
343 }
344 T_SETUPEND;
345 return (vm_address_t) tagged_ptr;
346 }
347
348 /*
349 * posix_spawn_with_flags_and_assert_successful_exit() will test the effect
350 * of dedicated flags to the legacy posix_spawnattr_set_use_sec_transition_shims_np()
351 * API. Pass: POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE to disable the default enablement of
352 * MTE and POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT to disable the default
353 * enablement of inheritance.
354 */
355 void
posix_spawn_with_flags_and_assert_successful_exit(char * const * args,posix_spawn_secflag_options flags,bool expect_mte,bool should_kill_child)356 posix_spawn_with_flags_and_assert_successful_exit(
357 char *const*args,
358 posix_spawn_secflag_options flags,
359 bool expect_mte,
360 bool should_kill_child)
361 {
362 pid_t child_pid = 0;
363 posix_spawnattr_t attr;
364 errno_t ret = posix_spawnattr_init(&attr);
365 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init");
366
367 ret = posix_spawnattr_set_use_sec_transition_shims_np(&attr, flags);
368 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_set_use_sec_transition_shims_np");
369
370 ret = posix_spawn(&child_pid, args[0], NULL, &attr, args, NULL);
371 T_ASSERT_POSIX_ZERO(ret, "posix_spawn");
372 T_ASSERT_NE(child_pid, 0, "posix_spawn");
373
374 validate_proc_pidinfo_mte_status(child_pid, expect_mte);
375
376 ret = posix_spawnattr_destroy(&attr);
377 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_destroy");
378
379 if (should_kill_child) {
380 kill_child(child_pid);
381 } else {
382 int status = -1;
383 T_ASSERT_POSIX_SUCCESS(waitpid(child_pid, &status, 0), "waitpid");
384 T_EXPECT_TRUE(WIFEXITED(status), "exited successfully");
385 T_EXPECT_TRUE(WEXITSTATUS(status) == 0, "exited with status %d", WEXITSTATUS(status));
386 }
387 }
388
389 void *
allocate_untagged_memory(mach_vm_size_t size)390 allocate_untagged_memory(mach_vm_size_t size)
391 {
392 mach_vm_address_t addr = 0;
393 T_LOG("Allocate untagged memory");
394 kern_return_t kr = mach_vm_allocate(
395 mach_task_self(),
396 &addr,
397 size,
398 VM_FLAGS_ANYWHERE );
399 T_ASSERT_MACH_SUCCESS(kr, "Allocated untagged page");
400 T_QUIET; T_ASSERT_NE_ULLONG(0ULL, addr, "Allocated address is not null");
401
402 void *untagged_ptr = (void *)addr;
403
404 void *still_untagged_ptr = __arm_mte_get_tag(untagged_ptr);
405 unsigned int orig_tag = extract_mte_tag(still_untagged_ptr);
406 T_QUIET; T_ASSERT_EQ_UINT(orig_tag, 0U,
407 "Assigned tag is zero, tag: %u", orig_tag);
408
409 return still_untagged_ptr;
410 }
411
412 uint64_t
sysctl_get_Q(const char * name)413 sysctl_get_Q(const char *name)
414 {
415 uint64_t v = 0;
416 size_t sz = sizeof(v);
417 int ret = sysctlbyname(name, &v, &sz, NULL, 0);
418 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed sysctl %s", name);
419 return v;
420 }
421