xref: /xnu-12377.41.6/tests/arm_mte_utilities.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
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