xref: /xnu-12377.61.12/tests/arm_mte_alias_restriction.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8) !
1*4d495c6eSApple OSS Distributions #if __arm64__
2*4d495c6eSApple OSS Distributions #include <arm_acle.h>
3*4d495c6eSApple OSS Distributions #include <darwintest.h>
4*4d495c6eSApple OSS Distributions #include <mach/mach.h>
5*4d495c6eSApple OSS Distributions #include <mach/vm_map.h>
6*4d495c6eSApple OSS Distributions #include <mach/mach_vm.h>
7*4d495c6eSApple OSS Distributions #include <sys/mman.h>
8*4d495c6eSApple OSS Distributions #include <spawn.h>
9*4d495c6eSApple OSS Distributions #include <unistd.h>
10*4d495c6eSApple OSS Distributions #include <stdlib.h>
11*4d495c6eSApple OSS Distributions #include <assert.h>
12*4d495c6eSApple OSS Distributions #include <stdio.h>
13*4d495c6eSApple OSS Distributions #include <sys/ptrace.h>
14*4d495c6eSApple OSS Distributions #include <fcntl.h>
15*4d495c6eSApple OSS Distributions 
16*4d495c6eSApple OSS Distributions #include "arm_mte_utilities.h"
17*4d495c6eSApple OSS Distributions #include "test_utils.h"
18*4d495c6eSApple OSS Distributions 
19*4d495c6eSApple OSS Distributions T_GLOBAL_META(
20*4d495c6eSApple OSS Distributions 	T_META_NAMESPACE("xnu.vm"),
21*4d495c6eSApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
22*4d495c6eSApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("VM"),
23*4d495c6eSApple OSS Distributions 	T_META_ENABLED(true),
24*4d495c6eSApple OSS Distributions 	XNU_T_META_SOC_SPECIFIC
25*4d495c6eSApple OSS Distributions 	);
26*4d495c6eSApple OSS Distributions 
27*4d495c6eSApple OSS Distributions /*
28*4d495c6eSApple OSS Distributions  * Processes which explicitly declare themselves as being unsafe to receive untagged aliases to tagged memory
29*4d495c6eSApple OSS Distributions  *  are killed by the system when the system conspires to grant them an untagged alias.
30*4d495c6eSApple OSS Distributions  * This test ensure that processes that opt into restrictions on aliasing tagged memory may not receive aliases:
31*4d495c6eSApple OSS Distributions  *  we launch a binary signed with an ID hard-coded into AMFI to opt in to this restriction, then attempt
32*4d495c6eSApple OSS Distributions  *  to remap tagged memory into this target.
33*4d495c6eSApple OSS Distributions  */
34*4d495c6eSApple OSS Distributions T_DECL(process_with_alias_restricted_opt_in_cannot_receive_mte_alias,
35*4d495c6eSApple OSS Distributions     "Ensure a process which opts in to alias restrictions may not receive "
36*4d495c6eSApple OSS Distributions     "an alias to tagged memory, and that an attempt to do so triggers a fatal guard.",
37*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
38*4d495c6eSApple OSS Distributions     XNU_T_META_SOC_SPECIFIC) {
39*4d495c6eSApple OSS Distributions 	/*
40*4d495c6eSApple OSS Distributions 	 * Given a binary signed in such a way that it should never be allowed to
41*4d495c6eSApple OSS Distributions 	 * receive aliases to untagged memory from elsewhere on the system.
42*4d495c6eSApple OSS Distributions 	 */
43*4d495c6eSApple OSS Distributions 
44*4d495c6eSApple OSS Distributions 	/* And we fork() so that we can observe the eventual SIGKILL */
45*4d495c6eSApple OSS Distributions 	expect_sigkill(^{
46*4d495c6eSApple OSS Distributions 		/* When we create some tagged memory in our context */
47*4d495c6eSApple OSS Distributions 		mach_vm_address_t address = 0;
48*4d495c6eSApple OSS Distributions 		mach_vm_size_t size = PAGE_SIZE;
49*4d495c6eSApple OSS Distributions 		kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
50*4d495c6eSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "allocate MTE memory");
51*4d495c6eSApple OSS Distributions 
52*4d495c6eSApple OSS Distributions 		/* And we spawn the process which is not allowed to receive aliases to tagged memory */
53*4d495c6eSApple OSS Distributions 		pid_t target_pid;
54*4d495c6eSApple OSS Distributions 		char* target_argv[] = {"arm_mte_alias_restriction_helper", NULL};
55*4d495c6eSApple OSS Distributions 		int ret = posix_spawn(&target_pid, target_argv[0], NULL, NULL, target_argv, NULL);
56*4d495c6eSApple OSS Distributions 		T_ASSERT_POSIX_ZERO(ret, "posix_spawn(%s)", target_argv[0]);
57*4d495c6eSApple OSS Distributions 		T_ASSERT_NE(target_pid, 0, "posix_spawn(%s)", target_argv[0]);
58*4d495c6eSApple OSS Distributions 
59*4d495c6eSApple OSS Distributions 		/* And we fetch a task port for the target */
60*4d495c6eSApple OSS Distributions 		task_port_t target_task_port;
61*4d495c6eSApple OSS Distributions 		kr = task_for_pid(mach_task_self(), target_pid, &target_task_port);
62*4d495c6eSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "task_for_pid for target");
63*4d495c6eSApple OSS Distributions 
64*4d495c6eSApple OSS Distributions 		/* When we attempt to remap the tagged memory into the target */
65*4d495c6eSApple OSS Distributions 		mach_vm_address_t remap_addr = 0;
66*4d495c6eSApple OSS Distributions 		vm_prot_t curprot = VM_PROT_WRITE | VM_PROT_READ;
67*4d495c6eSApple OSS Distributions 		vm_prot_t maxprot = VM_PROT_WRITE | VM_PROT_READ;
68*4d495c6eSApple OSS Distributions 		kr = mach_vm_remap_new(target_task_port, &remap_addr, size,
69*4d495c6eSApple OSS Distributions 		/* mask = */ 0, VM_FLAGS_ANYWHERE, mach_task_self(), address,
70*4d495c6eSApple OSS Distributions 		/* copy = */ FALSE, &curprot, &maxprot, VM_INHERIT_DEFAULT);
71*4d495c6eSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "remap tagged memory");
72*4d495c6eSApple OSS Distributions 		if (kr != KERN_SUCCESS) {
73*4d495c6eSApple OSS Distributions 		        fprintf(stderr, "failed to remap tagged memory\n");
74*4d495c6eSApple OSS Distributions 		        exit(1);
75*4d495c6eSApple OSS Distributions 		}
76*4d495c6eSApple OSS Distributions 
77*4d495c6eSApple OSS Distributions 		/* And we wire the memory in the target to trigger the policy check */
78*4d495c6eSApple OSS Distributions 		mach_port_t host_priv = HOST_PRIV_NULL;
79*4d495c6eSApple OSS Distributions 		kr = host_get_host_priv_port(mach_host_self(), &host_priv); \
80*4d495c6eSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "host_get_host_priv_port");
81*4d495c6eSApple OSS Distributions 		kr = mach_vm_wire(host_priv, target_task_port, remap_addr, size, VM_PROT_READ | VM_PROT_WRITE);
82*4d495c6eSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(kr, "mach_vm_wire in target");
83*4d495c6eSApple OSS Distributions 
84*4d495c6eSApple OSS Distributions 		/* Then the system should have killed the actor that attempted to enter this memory */
85*4d495c6eSApple OSS Distributions 		T_FAIL("Expected the system to prevent us from receiving an alias to tagged memory");
86*4d495c6eSApple OSS Distributions 	}, "Attempt to map an untagged alias to tagged memory in a restricted receiver");
87*4d495c6eSApple OSS Distributions }
88*4d495c6eSApple OSS Distributions 
89*4d495c6eSApple OSS Distributions T_DECL(vm_update_pointers_with_remote_tags_without_debugger,
90*4d495c6eSApple OSS Distributions     "Ensure mach_vm_update_pointers_with_remote_tags is unusable when not debugged",
91*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
92*4d495c6eSApple OSS Distributions     XNU_T_META_SOC_SPECIFIC) {
93*4d495c6eSApple OSS Distributions 	/* Given a tagged buffer */
94*4d495c6eSApple OSS Distributions 	const mach_vm_size_t alloc_size = PAGE_SIZE;
95*4d495c6eSApple OSS Distributions 	const mach_vm_size_t halfway = alloc_size / 2;
96*4d495c6eSApple OSS Distributions 	vm_address_t tagged_addr = allocate_and_tag_range(alloc_size, 0xa);
97*4d495c6eSApple OSS Distributions 	uint8_t* tagged_ptr = (uint8_t*)((uintptr_t)tagged_addr);
98*4d495c6eSApple OSS Distributions 	vm_address_t untagged_addr = tagged_addr & ~MTE_TAG_MASK;
99*4d495c6eSApple OSS Distributions 	uint8_t* untagged_ptr = (uint8_t*)((uintptr_t)untagged_addr);
100*4d495c6eSApple OSS Distributions 
101*4d495c6eSApple OSS Distributions 	mach_vm_offset_t addresses_to_tag[1] = {(mach_vm_offset_t)untagged_ptr};
102*4d495c6eSApple OSS Distributions 
103*4d495c6eSApple OSS Distributions 	/* And we grab a task port */
104*4d495c6eSApple OSS Distributions 	task_port_t target_task_port;
105*4d495c6eSApple OSS Distributions 	kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &target_task_port);
106*4d495c6eSApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "task_for_pid for target");
107*4d495c6eSApple OSS Distributions 
108*4d495c6eSApple OSS Distributions 	/* When we request the pointers be rewritten with their MTE tags */
109*4d495c6eSApple OSS Distributions 	mach_vm_offset_t resigned_addresses[1] = {0};
110*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t input_list = addresses_to_tag;
111*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t output_list = resigned_addresses;
112*4d495c6eSApple OSS Distributions 
113*4d495c6eSApple OSS Distributions 	int count = 1;
114*4d495c6eSApple OSS Distributions 	kr = mach_vm_update_pointers_with_remote_tags(
115*4d495c6eSApple OSS Distributions 		target_task_port,
116*4d495c6eSApple OSS Distributions 		input_list,
117*4d495c6eSApple OSS Distributions 		count,
118*4d495c6eSApple OSS Distributions 		output_list,
119*4d495c6eSApple OSS Distributions 		&count
120*4d495c6eSApple OSS Distributions 		);
121*4d495c6eSApple OSS Distributions 
122*4d495c6eSApple OSS Distributions 	/* Then it fails, because the input task wasn't debugged */
123*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ(kr, KERN_INVALID_ARGUMENT, "Expected mach_vm_update_pointers_with_remote_tags to fail when map !debugged");
124*4d495c6eSApple OSS Distributions }
125*4d495c6eSApple OSS Distributions 
126*4d495c6eSApple OSS Distributions T_DECL(vm_update_pointers_with_remote_tags_invalid_inputs,
127*4d495c6eSApple OSS Distributions     "Ensure mach_vm_update_pointers_with_remote_tags fails when the input sizes don't match",
128*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
129*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("security.mac.amfi.developer_mode_status", 1),
130*4d495c6eSApple OSS Distributions     /* It's not straightforward to ptrace on platforms other than macOS, so don't bother */
131*4d495c6eSApple OSS Distributions     T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX),
132*4d495c6eSApple OSS Distributions     XNU_T_META_SOC_SPECIFIC) {
133*4d495c6eSApple OSS Distributions 	/* Given we fork off into a debugger and debugee (and Developer Mode is enabled) */
134*4d495c6eSApple OSS Distributions 	/* And we set up a shared comms channel between parent and child */
135*4d495c6eSApple OSS Distributions 	int ret;
136*4d495c6eSApple OSS Distributions 	const char* memory_path = "vm_update_pointers";
137*4d495c6eSApple OSS Distributions 	shm_unlink(memory_path);
138*4d495c6eSApple OSS Distributions 	int shm_fd = shm_open(memory_path, O_RDWR | O_CREAT | O_EXCL);
139*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(shm_fd, "Created shared memory");
140*4d495c6eSApple OSS Distributions 
141*4d495c6eSApple OSS Distributions 	const mach_msg_type_number_t count = 1;
142*4d495c6eSApple OSS Distributions 	struct shared_data {
143*4d495c6eSApple OSS Distributions 		bool has_parent_connected;
144*4d495c6eSApple OSS Distributions 		bool has_child_populated_pointers;
145*4d495c6eSApple OSS Distributions 		bool has_parent_finished_inspecting_child;
146*4d495c6eSApple OSS Distributions 		bool has_child_acked_exit;
147*4d495c6eSApple OSS Distributions 		uint8_t* tagged_ptr;
148*4d495c6eSApple OSS Distributions 		uint8_t* differently_tagged_ptr;
149*4d495c6eSApple OSS Distributions 		mach_vm_offset_t addresses_to_tag[count];
150*4d495c6eSApple OSS Distributions 	};
151*4d495c6eSApple OSS Distributions 
152*4d495c6eSApple OSS Distributions 	ret = ftruncate(shm_fd, sizeof(struct shared_data));
153*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "ftruncate");
154*4d495c6eSApple OSS Distributions 	struct shared_data* shm = (struct shared_data*)mmap(NULL, sizeof(struct shared_data), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
155*4d495c6eSApple OSS Distributions 
156*4d495c6eSApple OSS Distributions 	const mach_vm_size_t alloc_size = PAGE_SIZE;
157*4d495c6eSApple OSS Distributions 	const mach_vm_size_t halfway = alloc_size / 2;
158*4d495c6eSApple OSS Distributions 
159*4d495c6eSApple OSS Distributions 	pid_t pid = fork();
160*4d495c6eSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
161*4d495c6eSApple OSS Distributions 
162*4d495c6eSApple OSS Distributions 	if (pid == 0) {
163*4d495c6eSApple OSS Distributions 		/* Allow the parent to attach */
164*4d495c6eSApple OSS Distributions 		while (!shm->has_parent_connected) {
165*4d495c6eSApple OSS Distributions 			sleep(1);
166*4d495c6eSApple OSS Distributions 		}
167*4d495c6eSApple OSS Distributions 
168*4d495c6eSApple OSS Distributions 		/* And a tagged buffer */
169*4d495c6eSApple OSS Distributions 		vm_address_t tagged_addr = allocate_and_tag_range(alloc_size, 0xa);
170*4d495c6eSApple OSS Distributions 		shm->tagged_ptr = (uint8_t*)((uintptr_t)tagged_addr);
171*4d495c6eSApple OSS Distributions 		vm_address_t untagged_addr = tagged_addr & ~MTE_TAG_MASK;
172*4d495c6eSApple OSS Distributions 		uint8_t* untagged_ptr = (uint8_t*)((uintptr_t)untagged_addr);
173*4d495c6eSApple OSS Distributions 
174*4d495c6eSApple OSS Distributions 		shm->addresses_to_tag[0] = (mach_vm_offset_t)&untagged_ptr[0];
175*4d495c6eSApple OSS Distributions 
176*4d495c6eSApple OSS Distributions 		/* Let the parent know we're ready to go */
177*4d495c6eSApple OSS Distributions 		shm->has_child_populated_pointers = true;
178*4d495c6eSApple OSS Distributions 
179*4d495c6eSApple OSS Distributions 		/* Allow the parent to interrogate our address space */
180*4d495c6eSApple OSS Distributions 		while (!shm->has_parent_finished_inspecting_child) {
181*4d495c6eSApple OSS Distributions 			sleep(1);
182*4d495c6eSApple OSS Distributions 		}
183*4d495c6eSApple OSS Distributions 		shm->has_child_acked_exit = true;
184*4d495c6eSApple OSS Distributions 		exit(0);
185*4d495c6eSApple OSS Distributions 	}
186*4d495c6eSApple OSS Distributions 
187*4d495c6eSApple OSS Distributions 	/* Attach to the child so it's marked as being debugged */
188*4d495c6eSApple OSS Distributions 	ret = ptrace(PT_ATTACHEXC, pid, 0, 0);
189*4d495c6eSApple OSS Distributions 	T_EXPECT_POSIX_SUCCESS(ret, "ptrace PT_ATTACHEXC");
190*4d495c6eSApple OSS Distributions 
191*4d495c6eSApple OSS Distributions 	/* And let the child know that it can carry on */
192*4d495c6eSApple OSS Distributions 	shm->has_parent_connected = true;
193*4d495c6eSApple OSS Distributions 
194*4d495c6eSApple OSS Distributions 	/* And ensure the child has set up the memory */
195*4d495c6eSApple OSS Distributions 	while (!(shm->has_child_populated_pointers)) {
196*4d495c6eSApple OSS Distributions 		sleep(1);
197*4d495c6eSApple OSS Distributions 	}
198*4d495c6eSApple OSS Distributions 
199*4d495c6eSApple OSS Distributions 	/* And we grab a task port */
200*4d495c6eSApple OSS Distributions 	task_port_t target_task_port;
201*4d495c6eSApple OSS Distributions 	kern_return_t kr = task_for_pid(mach_task_self(), pid, &target_task_port);
202*4d495c6eSApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "task_for_pid for target");
203*4d495c6eSApple OSS Distributions 
204*4d495c6eSApple OSS Distributions 	/* When we request the pointers be rewritten with their MTE tags */
205*4d495c6eSApple OSS Distributions 	mach_vm_offset_t resigned_addresses[count] = {0};
206*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t input_list = &shm->addresses_to_tag[0];
207*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t output_list = resigned_addresses;
208*4d495c6eSApple OSS Distributions 
209*4d495c6eSApple OSS Distributions 	/* But our output array has a size mismatched from the input array */
210*4d495c6eSApple OSS Distributions 	const mach_msg_type_number_t mismatched_count = 2;
211*4d495c6eSApple OSS Distributions 
212*4d495c6eSApple OSS Distributions 	kr = mach_vm_update_pointers_with_remote_tags(
213*4d495c6eSApple OSS Distributions 		target_task_port,
214*4d495c6eSApple OSS Distributions 		input_list,
215*4d495c6eSApple OSS Distributions 		count,
216*4d495c6eSApple OSS Distributions 		output_list,
217*4d495c6eSApple OSS Distributions 		&mismatched_count
218*4d495c6eSApple OSS Distributions 		);
219*4d495c6eSApple OSS Distributions 	/* Then it fails, because the input task wasn't debugged */
220*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ(kr, KERN_INVALID_ARGUMENT, "Expected mach_vm_update_pointers_with_remote_tags to fail input array sizes mismatch");
221*4d495c6eSApple OSS Distributions 
222*4d495c6eSApple OSS Distributions 	/* Cleanup: let the child know that it's fine to exit */
223*4d495c6eSApple OSS Distributions 	shm->has_parent_finished_inspecting_child = true;
224*4d495c6eSApple OSS Distributions 	while (!(shm->has_child_acked_exit)) {
225*4d495c6eSApple OSS Distributions 		sleep(1);
226*4d495c6eSApple OSS Distributions 	}
227*4d495c6eSApple OSS Distributions 
228*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(close(shm_fd), "Closed shm fd");
229*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(shm_unlink(memory_path), "Unlinked");
230*4d495c6eSApple OSS Distributions }
231*4d495c6eSApple OSS Distributions 
232*4d495c6eSApple OSS Distributions T_DECL(vm_update_pointers_with_remote_tags,
233*4d495c6eSApple OSS Distributions     "Validate the behavior of the API that allows reading remote tag info",
234*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
235*4d495c6eSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("security.mac.amfi.developer_mode_status", 1),
236*4d495c6eSApple OSS Distributions     /* It's not straightforward to ptrace on platforms other than macOS, so don't bother */
237*4d495c6eSApple OSS Distributions     T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX),
238*4d495c6eSApple OSS Distributions     XNU_T_META_SOC_SPECIFIC) {
239*4d495c6eSApple OSS Distributions 	/* Given we fork off into a debugger and debugee (and Developer Mode is enabled) */
240*4d495c6eSApple OSS Distributions 	/* And we set up a shared comms channel between parent and child */
241*4d495c6eSApple OSS Distributions 	int ret;
242*4d495c6eSApple OSS Distributions 	const char* memory_path = "vm_update_pointers";
243*4d495c6eSApple OSS Distributions 	shm_unlink(memory_path);
244*4d495c6eSApple OSS Distributions 	int shm_fd = shm_open(memory_path, O_RDWR | O_CREAT | O_EXCL);
245*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(shm_fd, "Created shared memory");
246*4d495c6eSApple OSS Distributions 
247*4d495c6eSApple OSS Distributions 	const mach_msg_type_number_t count = 4;
248*4d495c6eSApple OSS Distributions 	struct shared_data {
249*4d495c6eSApple OSS Distributions 		bool has_parent_connected;
250*4d495c6eSApple OSS Distributions 		bool has_child_populated_pointers;
251*4d495c6eSApple OSS Distributions 		bool has_parent_finished_inspecting_child;
252*4d495c6eSApple OSS Distributions 		bool has_child_acked_exit;
253*4d495c6eSApple OSS Distributions 		uint8_t* tagged_ptr;
254*4d495c6eSApple OSS Distributions 		uint8_t* differently_tagged_ptr;
255*4d495c6eSApple OSS Distributions 		uint8_t* untagged_ptr;
256*4d495c6eSApple OSS Distributions 		mach_vm_offset_t addresses_to_tag[count];
257*4d495c6eSApple OSS Distributions 	};
258*4d495c6eSApple OSS Distributions 
259*4d495c6eSApple OSS Distributions 	ret = ftruncate(shm_fd, sizeof(struct shared_data));
260*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "ftruncate");
261*4d495c6eSApple OSS Distributions 	struct shared_data* shm = (struct shared_data*)mmap(NULL, sizeof(struct shared_data), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
262*4d495c6eSApple OSS Distributions 
263*4d495c6eSApple OSS Distributions 	const mach_vm_size_t alloc_size = PAGE_SIZE;
264*4d495c6eSApple OSS Distributions 	const mach_vm_size_t halfway = alloc_size / 2;
265*4d495c6eSApple OSS Distributions 
266*4d495c6eSApple OSS Distributions 	pid_t pid = fork();
267*4d495c6eSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork");
268*4d495c6eSApple OSS Distributions 
269*4d495c6eSApple OSS Distributions 	if (pid == 0) {
270*4d495c6eSApple OSS Distributions 		/* Allow the parent to attach */
271*4d495c6eSApple OSS Distributions 		while (!shm->has_parent_connected) {
272*4d495c6eSApple OSS Distributions 			sleep(1);
273*4d495c6eSApple OSS Distributions 		}
274*4d495c6eSApple OSS Distributions 
275*4d495c6eSApple OSS Distributions 		/* Given an untagged buffer */
276*4d495c6eSApple OSS Distributions 		void* untagged_buffer_addr = allocate_untagged_memory(alloc_size);
277*4d495c6eSApple OSS Distributions 		shm->untagged_ptr = untagged_buffer_addr;
278*4d495c6eSApple OSS Distributions 
279*4d495c6eSApple OSS Distributions 		/* And a tagged buffer */
280*4d495c6eSApple OSS Distributions 		vm_address_t tagged_addr = allocate_and_tag_range(alloc_size, 0xa);
281*4d495c6eSApple OSS Distributions 		shm->tagged_ptr = (uint8_t*)((uintptr_t)tagged_addr);
282*4d495c6eSApple OSS Distributions 		vm_address_t untagged_addr = tagged_addr & ~MTE_TAG_MASK;
283*4d495c6eSApple OSS Distributions 		uint8_t* untagged_ptr = (uint8_t*)((uintptr_t)untagged_addr);
284*4d495c6eSApple OSS Distributions 
285*4d495c6eSApple OSS Distributions 		/* And a different tag is used halfway through the tagged buffer */
286*4d495c6eSApple OSS Distributions 		uint64_t different_tag = 0xb;
287*4d495c6eSApple OSS Distributions 		shm->differently_tagged_ptr = (uint8_t*)((uintptr_t) untagged_ptr | (different_tag << MTE_TAG_SHIFT));
288*4d495c6eSApple OSS Distributions 		for (mach_vm_size_t offset = halfway; offset < alloc_size; offset += MTE_GRANULE_SIZE) {
289*4d495c6eSApple OSS Distributions 			__arm_mte_set_tag(&shm->differently_tagged_ptr[offset]);
290*4d495c6eSApple OSS Distributions 		}
291*4d495c6eSApple OSS Distributions 
292*4d495c6eSApple OSS Distributions 		/* And a pointer to a bogus address */
293*4d495c6eSApple OSS Distributions 		mach_vm_offset_t bogus_pointer = 0xaaaaaaaa;
294*4d495c6eSApple OSS Distributions 		/* And one of the pointers points to a region that we try ensure isn't resident */
295*4d495c6eSApple OSS Distributions 		T_ASSERT_POSIX_ZERO(madvise(untagged_buffer_addr, alloc_size, MADV_DONTNEED), "madvise(DONTNEED)");
296*4d495c6eSApple OSS Distributions 
297*4d495c6eSApple OSS Distributions 		shm->addresses_to_tag[0] = (mach_vm_offset_t)&untagged_ptr[0];
298*4d495c6eSApple OSS Distributions 		shm->addresses_to_tag[1] = (mach_vm_offset_t)&untagged_ptr[halfway];
299*4d495c6eSApple OSS Distributions 		shm->addresses_to_tag[2] = (mach_vm_offset_t)untagged_buffer_addr;
300*4d495c6eSApple OSS Distributions 		shm->addresses_to_tag[3] = (mach_vm_offset_t)bogus_pointer;
301*4d495c6eSApple OSS Distributions 
302*4d495c6eSApple OSS Distributions 		/* Let the parent know we're ready to go */
303*4d495c6eSApple OSS Distributions 		shm->has_child_populated_pointers = true;
304*4d495c6eSApple OSS Distributions 
305*4d495c6eSApple OSS Distributions 		/* Allow the parent to interrogate our address space */
306*4d495c6eSApple OSS Distributions 		while (!shm->has_parent_finished_inspecting_child) {
307*4d495c6eSApple OSS Distributions 			sleep(1);
308*4d495c6eSApple OSS Distributions 		}
309*4d495c6eSApple OSS Distributions 		shm->has_child_acked_exit = true;
310*4d495c6eSApple OSS Distributions 		exit(0);
311*4d495c6eSApple OSS Distributions 	}
312*4d495c6eSApple OSS Distributions 
313*4d495c6eSApple OSS Distributions 	/* Attach to the child so it's marked as being debugged */
314*4d495c6eSApple OSS Distributions 	ret = ptrace(PT_ATTACHEXC, pid, 0, 0);
315*4d495c6eSApple OSS Distributions 	T_EXPECT_POSIX_SUCCESS(ret, "ptrace PT_ATTACHEXC");
316*4d495c6eSApple OSS Distributions 
317*4d495c6eSApple OSS Distributions 	/* And let the child know that it can carry on */
318*4d495c6eSApple OSS Distributions 	shm->has_parent_connected = true;
319*4d495c6eSApple OSS Distributions 
320*4d495c6eSApple OSS Distributions 	/* And ensure the child has set up the memory */
321*4d495c6eSApple OSS Distributions 	while (!(shm->has_child_populated_pointers)) {
322*4d495c6eSApple OSS Distributions 		sleep(1);
323*4d495c6eSApple OSS Distributions 	}
324*4d495c6eSApple OSS Distributions 
325*4d495c6eSApple OSS Distributions 	/* And we grab a task port */
326*4d495c6eSApple OSS Distributions 	task_port_t target_task_port;
327*4d495c6eSApple OSS Distributions 	kern_return_t kr = task_for_pid(mach_task_self(), pid, &target_task_port);
328*4d495c6eSApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "task_for_pid for target");
329*4d495c6eSApple OSS Distributions 
330*4d495c6eSApple OSS Distributions 	/* When we request the pointers be rewritten with their MTE tags */
331*4d495c6eSApple OSS Distributions 	mach_vm_offset_t resigned_addresses[count] = {0};
332*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t input_list = &shm->addresses_to_tag[0];
333*4d495c6eSApple OSS Distributions 	mach_vm_offset_list_t output_list = resigned_addresses;
334*4d495c6eSApple OSS Distributions 
335*4d495c6eSApple OSS Distributions 	kr = mach_vm_update_pointers_with_remote_tags(
336*4d495c6eSApple OSS Distributions 		target_task_port,
337*4d495c6eSApple OSS Distributions 		input_list,
338*4d495c6eSApple OSS Distributions 		count,
339*4d495c6eSApple OSS Distributions 		output_list,
340*4d495c6eSApple OSS Distributions 		&count
341*4d495c6eSApple OSS Distributions 		);
342*4d495c6eSApple OSS Distributions 	T_ASSERT_MACH_SUCCESS(kr, "mach_vm_update_pointers_with_remote_tags");
343*4d495c6eSApple OSS Distributions 
344*4d495c6eSApple OSS Distributions 	/* Then the pointers have been rewritten as expected */
345*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ_ULONG((unsigned long)output_list[0], (unsigned long)&(shm->tagged_ptr)[0], "Expected pointer 1 to be correctly rewritten");
346*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ_ULONG((unsigned long)output_list[1], (unsigned long)&(shm->differently_tagged_ptr)[halfway], "Expected pointer 2 to be correctly rewritten");
347*4d495c6eSApple OSS Distributions 	/* A non-MTE-enabled object is returned as-is in the output list */
348*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ_ULONG((unsigned long)output_list[2], shm->untagged_ptr, "Expected a non-MTE address to be returned as-is");
349*4d495c6eSApple OSS Distributions 	/* An invalid input pointer is returned as zero in the output list */
350*4d495c6eSApple OSS Distributions 	T_ASSERT_EQ_ULONG((unsigned long)output_list[3], 0, "Expected an unmapped address to be transformed to 0");
351*4d495c6eSApple OSS Distributions 
352*4d495c6eSApple OSS Distributions 	/* And let the child know that it's fine to exit */
353*4d495c6eSApple OSS Distributions 	shm->has_parent_finished_inspecting_child = true;
354*4d495c6eSApple OSS Distributions 	while (!(shm->has_child_acked_exit)) {
355*4d495c6eSApple OSS Distributions 		sleep(1);
356*4d495c6eSApple OSS Distributions 	}
357*4d495c6eSApple OSS Distributions 
358*4d495c6eSApple OSS Distributions 	/* Cleanup */
359*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(close(shm_fd), "Closed shm fd");
360*4d495c6eSApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(shm_unlink(memory_path), "Unlinked");
361*4d495c6eSApple OSS Distributions }
362*4d495c6eSApple OSS Distributions #endif /* __arm64__ */
363