xref: /xnu-12377.81.4/tests/vm/upl.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1*043036a2SApple OSS Distributions /*
2*043036a2SApple OSS Distributions  * Copyright (c) 2024 Apple Inc. All rights reserved.
3*043036a2SApple OSS Distributions  *
4*043036a2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*043036a2SApple OSS Distributions  *
6*043036a2SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*043036a2SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*043036a2SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*043036a2SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*043036a2SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*043036a2SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*043036a2SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*043036a2SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*043036a2SApple OSS Distributions  *
15*043036a2SApple OSS Distributions  * Please obtain a copy of the License at
16*043036a2SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*043036a2SApple OSS Distributions  *
18*043036a2SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*043036a2SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*043036a2SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*043036a2SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*043036a2SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*043036a2SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*043036a2SApple OSS Distributions  * limitations under the License.
25*043036a2SApple OSS Distributions  *
26*043036a2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*043036a2SApple OSS Distributions  *
28*043036a2SApple OSS Distributions  */
29*043036a2SApple OSS Distributions 
30*043036a2SApple OSS Distributions #include <darwintest.h>
31*043036a2SApple OSS Distributions #include <darwintest_utils.h>
32*043036a2SApple OSS Distributions 
33*043036a2SApple OSS Distributions #include <os/thread_self_restrict.h>
34*043036a2SApple OSS Distributions 
35*043036a2SApple OSS Distributions #include <stdlib.h>
36*043036a2SApple OSS Distributions #include <sys/mman.h>
37*043036a2SApple OSS Distributions 
38*043036a2SApple OSS Distributions #include <mach/mach.h>
39*043036a2SApple OSS Distributions #include <mach/mach_vm.h>
40*043036a2SApple OSS Distributions 
41*043036a2SApple OSS Distributions #include <System/machine/cpu_capabilities.h>
42*043036a2SApple OSS Distributions 
43*043036a2SApple OSS Distributions #include "exc_guard_helper.h"
44*043036a2SApple OSS Distributions #include "test_utils.h"
45*043036a2SApple OSS Distributions 
46*043036a2SApple OSS Distributions T_GLOBAL_META(
47*043036a2SApple OSS Distributions 	T_META_NAMESPACE("xnu.vm"),
48*043036a2SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
49*043036a2SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("VM"),
50*043036a2SApple OSS Distributions 	T_META_OWNER("jharmening"),
51*043036a2SApple OSS Distributions 	T_META_CHECK_LEAKS(false),
52*043036a2SApple OSS Distributions 	T_META_RUN_CONCURRENTLY(true),
53*043036a2SApple OSS Distributions 	T_META_ALL_VALID_ARCHS(true));
54*043036a2SApple OSS Distributions 
55*043036a2SApple OSS Distributions typedef struct {
56*043036a2SApple OSS Distributions 	uint64_t ptr;
57*043036a2SApple OSS Distributions 	uint32_t size;
58*043036a2SApple OSS Distributions 	char test_pattern;
59*043036a2SApple OSS Distributions 	bool copy_expected;
60*043036a2SApple OSS Distributions 	bool should_fail;
61*043036a2SApple OSS Distributions 	bool upl_rw;
62*043036a2SApple OSS Distributions } upl_test_args;
63*043036a2SApple OSS Distributions 
64*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_rw,
65*043036a2SApple OSS Distributions     "Generate RO UPL against RW memory region")
66*043036a2SApple OSS Distributions {
67*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
68*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
69*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
70*043036a2SApple OSS Distributions 
71*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
72*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
73*043036a2SApple OSS Distributions 	}
74*043036a2SApple OSS Distributions 
75*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'a',
76*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = false, .upl_rw = false };
77*043036a2SApple OSS Distributions 
78*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
79*043036a2SApple OSS Distributions 	int64_t result = 0;
80*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
81*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
82*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
83*043036a2SApple OSS Distributions 
84*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x800;
85*043036a2SApple OSS Distributions 	args.size -= 0x1000;
86*043036a2SApple OSS Distributions 
87*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
88*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
89*043036a2SApple OSS Distributions 
90*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x1000;
91*043036a2SApple OSS Distributions 	args.size -= 0x1000;
92*043036a2SApple OSS Distributions 
93*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
94*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
95*043036a2SApple OSS Distributions 
96*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
97*043036a2SApple OSS Distributions }
98*043036a2SApple OSS Distributions 
99*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_ro,
100*043036a2SApple OSS Distributions     "Generate RO UPL against RO memory region")
101*043036a2SApple OSS Distributions {
102*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
103*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
104*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
105*043036a2SApple OSS Distributions 
106*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
107*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
108*043036a2SApple OSS Distributions 	}
109*043036a2SApple OSS Distributions 
110*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(buf, buf_size, PROT_READ), "mprotect");
111*043036a2SApple OSS Distributions 
112*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'a',
113*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = false, .upl_rw = false };
114*043036a2SApple OSS Distributions 
115*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
116*043036a2SApple OSS Distributions 	int64_t result = 0;
117*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
118*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
119*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
120*043036a2SApple OSS Distributions 
121*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x800;
122*043036a2SApple OSS Distributions 	args.size -= 0x1000;
123*043036a2SApple OSS Distributions 
124*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
125*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
126*043036a2SApple OSS Distributions 
127*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x1000;
128*043036a2SApple OSS Distributions 	args.size -= 0x1000;
129*043036a2SApple OSS Distributions 
130*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
131*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
132*043036a2SApple OSS Distributions 
133*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
134*043036a2SApple OSS Distributions }
135*043036a2SApple OSS Distributions 
136*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_rw,
137*043036a2SApple OSS Distributions     "Generate RW UPL against RW memory region")
138*043036a2SApple OSS Distributions {
139*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
140*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
141*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
142*043036a2SApple OSS Distributions 
143*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
144*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
145*043036a2SApple OSS Distributions 	}
146*043036a2SApple OSS Distributions 
147*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b',
148*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = false, .upl_rw = true };
149*043036a2SApple OSS Distributions 
150*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
151*043036a2SApple OSS Distributions 	int64_t result = 0;
152*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
153*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
154*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
155*043036a2SApple OSS Distributions 
156*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
157*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i,
158*043036a2SApple OSS Distributions 		    "buf[%u]='%u' == '%u'",
159*043036a2SApple OSS Distributions 		    i, buf[i], (unsigned int)'b' + i);
160*043036a2SApple OSS Distributions 	}
161*043036a2SApple OSS Distributions 	bzero(buf, buf_size);
162*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x800;
163*043036a2SApple OSS Distributions 	args.size -= 0x1000;
164*043036a2SApple OSS Distributions 
165*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
166*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
167*043036a2SApple OSS Distributions 
168*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
169*043036a2SApple OSS Distributions 		if ((i < (0x800 / sizeof(*buf))) || (i >= ((0x800 + args.size) / sizeof(*buf)))) {
170*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], 0,
171*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == 0", i, buf[i]);
172*043036a2SApple OSS Distributions 		} else {
173*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)),
174*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == '%u'",
175*043036a2SApple OSS Distributions 			    i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)));
176*043036a2SApple OSS Distributions 		}
177*043036a2SApple OSS Distributions 	}
178*043036a2SApple OSS Distributions 
179*043036a2SApple OSS Distributions 	bzero(buf, buf_size);
180*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x1000;
181*043036a2SApple OSS Distributions 	args.size -= 0x1000;
182*043036a2SApple OSS Distributions 
183*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
184*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
185*043036a2SApple OSS Distributions 
186*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
187*043036a2SApple OSS Distributions 		if ((i < (0x1000 / sizeof(*buf))) || (i >= ((0x1000 + args.size) / sizeof(*buf)))) {
188*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], 0,
189*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == 0", i, buf[i]);
190*043036a2SApple OSS Distributions 		} else {
191*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x1000 / sizeof(*buf)),
192*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == '%u'",
193*043036a2SApple OSS Distributions 			    i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x1000 / sizeof(*buf)));
194*043036a2SApple OSS Distributions 		}
195*043036a2SApple OSS Distributions 	}
196*043036a2SApple OSS Distributions 
197*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
198*043036a2SApple OSS Distributions }
199*043036a2SApple OSS Distributions 
200*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_ro,
201*043036a2SApple OSS Distributions     "Generate RW UPL against RO memory region")
202*043036a2SApple OSS Distributions {
203*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
204*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
205*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
206*043036a2SApple OSS Distributions 
207*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect(buf, buf_size, PROT_READ), "mprotect");
208*043036a2SApple OSS Distributions 
209*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b',
210*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = true, .upl_rw = true };
211*043036a2SApple OSS Distributions 
212*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
213*043036a2SApple OSS Distributions 	int64_t result = 0;
214*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
215*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
216*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
217*043036a2SApple OSS Distributions 
218*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x800;
219*043036a2SApple OSS Distributions 	args.size -= 0x1000;
220*043036a2SApple OSS Distributions 
221*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
222*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
223*043036a2SApple OSS Distributions 
224*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x1000;
225*043036a2SApple OSS Distributions 	args.size -= 0x1000;
226*043036a2SApple OSS Distributions 
227*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
228*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
229*043036a2SApple OSS Distributions 
230*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
231*043036a2SApple OSS Distributions }
232*043036a2SApple OSS Distributions 
233*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_rx,
234*043036a2SApple OSS Distributions     "Generate RO UPL against RX memory region")
235*043036a2SApple OSS Distributions {
236*043036a2SApple OSS Distributions 	bool copy_expected = true;
237*043036a2SApple OSS Distributions #if TARGET_OS_OSX
238*043036a2SApple OSS Distributions 	/**
239*043036a2SApple OSS Distributions 	 * For embedded targets, UPL creation against RX mappings should always produce a copy due to codesigning.
240*043036a2SApple OSS Distributions 	 * For MacOS, a copy should only be produced if the SPTM is enabled, due to the SPTM's stricter requirements
241*043036a2SApple OSS Distributions 	 * for DMA mappings of executable frame types.
242*043036a2SApple OSS Distributions 	 */
243*043036a2SApple OSS Distributions 	if (!is_sptm_enabled()) {
244*043036a2SApple OSS Distributions 		copy_expected = false;
245*043036a2SApple OSS Distributions 	}
246*043036a2SApple OSS Distributions #endif /* TARGET_OS_OSX */
247*043036a2SApple OSS Distributions 
248*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)__builtin_return_address(0), .size = PAGE_SIZE, .test_pattern = 'a',
249*043036a2SApple OSS Distributions 		               .copy_expected = copy_expected, .should_fail = false, .upl_rw = false };
250*043036a2SApple OSS Distributions 
251*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
252*043036a2SApple OSS Distributions 	int64_t result = 0;
253*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
254*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
255*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
256*043036a2SApple OSS Distributions 
257*043036a2SApple OSS Distributions 	args.ptr += 0x100;
258*043036a2SApple OSS Distributions 	args.size -= 0x200;
259*043036a2SApple OSS Distributions 
260*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
261*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
262*043036a2SApple OSS Distributions }
263*043036a2SApple OSS Distributions 
264*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_rx,
265*043036a2SApple OSS Distributions     "Generate RW UPL against RX memory region")
266*043036a2SApple OSS Distributions {
267*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)__builtin_return_address(0), .size = PAGE_SIZE, .test_pattern = 'a',
268*043036a2SApple OSS Distributions 		               .copy_expected = true, .should_fail = true, .upl_rw = true };
269*043036a2SApple OSS Distributions 
270*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
271*043036a2SApple OSS Distributions 	int64_t result = 0;
272*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
273*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
274*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
275*043036a2SApple OSS Distributions 
276*043036a2SApple OSS Distributions 	args.ptr += 0x100;
277*043036a2SApple OSS Distributions 	args.size -= 0x200;
278*043036a2SApple OSS Distributions 
279*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
280*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
281*043036a2SApple OSS Distributions }
282*043036a2SApple OSS Distributions 
283*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_jit,
284*043036a2SApple OSS Distributions     "Generate RO UPL against JIT memory region")
285*043036a2SApple OSS Distributions {
286*043036a2SApple OSS Distributions 	if (!is_map_jit_allowed()) {
287*043036a2SApple OSS Distributions 		T_SKIP("MAP_JIT not allowed for this system configuration");
288*043036a2SApple OSS Distributions 	}
289*043036a2SApple OSS Distributions 	/**
290*043036a2SApple OSS Distributions 	 * Direct RO UPLs against JIT pages should be allowed for non-SPTM targets.
291*043036a2SApple OSS Distributions 	 * For SPTM targets, a copy is expected due to the SPTM's stricter requirements for DMA
292*043036a2SApple OSS Distributions 	 * mappings of executable frame types.
293*043036a2SApple OSS Distributions 	 */
294*043036a2SApple OSS Distributions 	bool copy_expected = is_sptm_enabled();
295*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
296*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);
297*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
298*043036a2SApple OSS Distributions 
299*043036a2SApple OSS Distributions 	if (os_thread_self_restrict_rwx_is_supported()) {
300*043036a2SApple OSS Distributions 		os_thread_self_restrict_rwx_to_rw();
301*043036a2SApple OSS Distributions 	}
302*043036a2SApple OSS Distributions 
303*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
304*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
305*043036a2SApple OSS Distributions 	}
306*043036a2SApple OSS Distributions 
307*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b',
308*043036a2SApple OSS Distributions 		               .copy_expected = copy_expected, .should_fail = false, .upl_rw = false };
309*043036a2SApple OSS Distributions 
310*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
311*043036a2SApple OSS Distributions 	int64_t result = 0;
312*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
313*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
314*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
315*043036a2SApple OSS Distributions 
316*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x800;
317*043036a2SApple OSS Distributions 	args.size -= 0x1000;
318*043036a2SApple OSS Distributions 
319*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
320*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
321*043036a2SApple OSS Distributions 
322*043036a2SApple OSS Distributions 	args.ptr = (uint64_t)buf + 0x1000;
323*043036a2SApple OSS Distributions 	args.size -= 0x1000;
324*043036a2SApple OSS Distributions 
325*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
326*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
327*043036a2SApple OSS Distributions 
328*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
329*043036a2SApple OSS Distributions }
330*043036a2SApple OSS Distributions 
331*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_jit,
332*043036a2SApple OSS Distributions     "Generate RW UPL against JIT memory region")
333*043036a2SApple OSS Distributions {
334*043036a2SApple OSS Distributions 	if (process_is_translated()) {
335*043036a2SApple OSS Distributions 		/* TODO: Remove this once rdar://142438840 is fixed. */
336*043036a2SApple OSS Distributions 		T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping...");
337*043036a2SApple OSS Distributions 	}
338*043036a2SApple OSS Distributions 	if (!is_map_jit_allowed()) {
339*043036a2SApple OSS Distributions 		T_SKIP("MAP_JIT not allowed for this system configuration");
340*043036a2SApple OSS Distributions 	}
341*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
342*043036a2SApple OSS Distributions 	/**
343*043036a2SApple OSS Distributions 	 * Direct RW UPLs against JIT pages should be allowed for non-SPTM targets.
344*043036a2SApple OSS Distributions 	 * For SPTM targets, UPL creation should fail due to the SPTM's stricter requirements for DMA
345*043036a2SApple OSS Distributions 	 * mappings of executable frame types.
346*043036a2SApple OSS Distributions 	 */
347*043036a2SApple OSS Distributions 	bool should_fail = is_sptm_enabled();
348*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);
349*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
350*043036a2SApple OSS Distributions 
351*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .test_pattern = 'b',
352*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = should_fail, .upl_rw = true };
353*043036a2SApple OSS Distributions 
354*043036a2SApple OSS Distributions 	__block int64_t addr = (int64_t)&args;
355*043036a2SApple OSS Distributions 	__block int64_t result = 0;
356*043036a2SApple OSS Distributions 	__block size_t s = sizeof(result);
357*043036a2SApple OSS Distributions 
358*043036a2SApple OSS Distributions 	/* Ensure that guard exceptions will not be fatal to the test process. */
359*043036a2SApple OSS Distributions 	enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY);
360*043036a2SApple OSS Distributions 
361*043036a2SApple OSS Distributions 	/**
362*043036a2SApple OSS Distributions 	 * Iterate 3 times to guarantee buffer offsets that are neither 4K nor 16K aligned,
363*043036a2SApple OSS Distributions 	 * and 4K but not necessarily 16K aligned.
364*043036a2SApple OSS Distributions 	 */
365*043036a2SApple OSS Distributions 	for (int i = 0; i < 2; i++) {
366*043036a2SApple OSS Distributions 		exc_guard_helper_info_t exc_info;
367*043036a2SApple OSS Distributions 		bool caught_exception =
368*043036a2SApple OSS Distributions 		    block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{
369*043036a2SApple OSS Distributions 			T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
370*043036a2SApple OSS Distributions 			"sysctlbyname(debug.test.vm_upl)");
371*043036a2SApple OSS Distributions 		});
372*043036a2SApple OSS Distributions 		if (args.should_fail) {
373*043036a2SApple OSS Distributions 			T_ASSERT_TRUE(caught_exception, "Failing test should also throw guard exception");
374*043036a2SApple OSS Distributions 			T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_UPL_WRITE_ON_EXEC_REGION,
375*043036a2SApple OSS Distributions 			    "Failing test throws the expected guard exception flavor");
376*043036a2SApple OSS Distributions 			T_ASSERT_EQ(exc_info.catch_count, 1, "Failing test should throw exactly one guard exception");
377*043036a2SApple OSS Distributions 		} else {
378*043036a2SApple OSS Distributions 			T_ASSERT_FALSE(caught_exception, "Passing test should not throw guard exception");
379*043036a2SApple OSS Distributions 		}
380*043036a2SApple OSS Distributions 
381*043036a2SApple OSS Distributions 		args.ptr += 0x800;
382*043036a2SApple OSS Distributions 		args.size -= 0x1000;
383*043036a2SApple OSS Distributions 	}
384*043036a2SApple OSS Distributions 
385*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
386*043036a2SApple OSS Distributions }
387*043036a2SApple OSS Distributions 
388*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_commpage,
389*043036a2SApple OSS Distributions     "Generate RO UPL against comm page")
390*043036a2SApple OSS Distributions {
391*043036a2SApple OSS Distributions #if !TARGET_OS_OSX
392*043036a2SApple OSS Distributions 	T_SKIP("Comm page only guaranteed to be within user address range on MacOS, skipping...");
393*043036a2SApple OSS Distributions #else
394*043036a2SApple OSS Distributions #ifndef __arm64__
395*043036a2SApple OSS Distributions 	T_SKIP("Comm page only has UPL-incompatible mapping on arm64, skipping...");
396*043036a2SApple OSS Distributions #else
397*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)_COMM_PAGE_START_ADDRESS, .size = 0x1000, .test_pattern = 'b',
398*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = true, .upl_rw = false };
399*043036a2SApple OSS Distributions 
400*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
401*043036a2SApple OSS Distributions 	int64_t result = 0;
402*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
403*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
404*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
405*043036a2SApple OSS Distributions #endif /* !defined(__arm64__) */
406*043036a2SApple OSS Distributions #endif /* !TARGET_OS_OSX */
407*043036a2SApple OSS Distributions }
408*043036a2SApple OSS Distributions 
409*043036a2SApple OSS Distributions T_DECL(vm_upl_partial_cow,
410*043036a2SApple OSS Distributions     "Generate a UPL that requires CoW setup for part of an object")
411*043036a2SApple OSS Distributions {
412*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
413*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
414*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
415*043036a2SApple OSS Distributions 
416*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
417*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
418*043036a2SApple OSS Distributions 	}
419*043036a2SApple OSS Distributions 
420*043036a2SApple OSS Distributions 	/*
421*043036a2SApple OSS Distributions 	 * Mark a portion of the buffer RO, which will split off a separate vm_map_entry backed by the same
422*043036a2SApple OSS Distributions 	 * vm_object.  This will produce an internal COPY_SYMMETRIC object with refcount > 1, which is the
423*043036a2SApple OSS Distributions 	 * baseline requirement for partial CoW setup by vm_map_create_upl().
424*043036a2SApple OSS Distributions 	 */
425*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(mprotect((char*)buf + (8 * PAGE_SIZE), 2 * PAGE_SIZE, PROT_READ), "mprotect");
426*043036a2SApple OSS Distributions 
427*043036a2SApple OSS Distributions 	/*
428*043036a2SApple OSS Distributions 	 * Request a non-page-aligned UPL against the RW region of the buffer, to ensure that partial CoW
429*043036a2SApple OSS Distributions 	 * setup still ultimately uses a page-aligned buffer as required for vm_map_entry clipping.
430*043036a2SApple OSS Distributions 	 */
431*043036a2SApple OSS Distributions 	upl_test_args args = { .ptr = (uint64_t)buf + 0x800, .size = 2 * PAGE_SIZE, .test_pattern = 'b',
432*043036a2SApple OSS Distributions 		               .copy_expected = false, .should_fail = false, .upl_rw = true };
433*043036a2SApple OSS Distributions 
434*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
435*043036a2SApple OSS Distributions 	int64_t result = 0;
436*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
437*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl", &result, &s, &addr, sizeof(addr)),
438*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl)");
439*043036a2SApple OSS Distributions 
440*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
441*043036a2SApple OSS Distributions 		if ((i < (0x800 / sizeof(*buf))) || (i >= ((0x800 + args.size) / sizeof(*buf)))) {
442*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'a' + i,
443*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == '%u'", i, buf[i], (unsigned int)'a' + i);
444*043036a2SApple OSS Distributions 		} else {
445*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_EQ(buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)),
446*043036a2SApple OSS Distributions 			    "buf[%u]='%u' == '%u'",
447*043036a2SApple OSS Distributions 			    i, buf[i], (unsigned int)'b' + i - (unsigned int)(0x800 / sizeof(*buf)));
448*043036a2SApple OSS Distributions 		}
449*043036a2SApple OSS Distributions 	}
450*043036a2SApple OSS Distributions 
451*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
452*043036a2SApple OSS Distributions }
453*043036a2SApple OSS Distributions 
454*043036a2SApple OSS Distributions typedef struct {
455*043036a2SApple OSS Distributions 	uint64_t ptr;
456*043036a2SApple OSS Distributions 	uint32_t size;
457*043036a2SApple OSS Distributions 	bool upl_rw;
458*043036a2SApple OSS Distributions 	bool should_fail;
459*043036a2SApple OSS Distributions 	uint8_t fault_prot;
460*043036a2SApple OSS Distributions } upl_object_test_args;
461*043036a2SApple OSS Distributions 
462*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_exec_object,
463*043036a2SApple OSS Distributions     "Attempt to create a writable UPL against an object containing executable pages")
464*043036a2SApple OSS Distributions {
465*043036a2SApple OSS Distributions 	/**
466*043036a2SApple OSS Distributions 	 * This test is meant to exercise functionality that is currently SPTM-specific.
467*043036a2SApple OSS Distributions 	 * It also relies on the assumption that JIT regions are faulted in an all-or-nothing
468*043036a2SApple OSS Distributions 	 * manner, so that the write faults generated by our buffer fill below will also
469*043036a2SApple OSS Distributions 	 * produce executable mappings of the underlying JIT pages.  This happens to hold
470*043036a2SApple OSS Distributions 	 * true on SPTM-enabled devices because all of them use xPRR, but may not hold true
471*043036a2SApple OSS Distributions 	 * in general.
472*043036a2SApple OSS Distributions 	 */
473*043036a2SApple OSS Distributions 	if (!is_sptm_enabled()) {
474*043036a2SApple OSS Distributions 		T_SKIP("Exec object test only supported on SPTM-enabled devices, skipping...");
475*043036a2SApple OSS Distributions 	}
476*043036a2SApple OSS Distributions 	if (process_is_translated()) {
477*043036a2SApple OSS Distributions 		/* TODO: Remove this once rdar://142438840 is fixed. */
478*043036a2SApple OSS Distributions 		T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping...");
479*043036a2SApple OSS Distributions 	}
480*043036a2SApple OSS Distributions 	if (!is_map_jit_allowed()) {
481*043036a2SApple OSS Distributions 		T_SKIP("MAP_JIT not allowed for this system configuration");
482*043036a2SApple OSS Distributions 	}
483*043036a2SApple OSS Distributions 
484*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
485*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);
486*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
487*043036a2SApple OSS Distributions 
488*043036a2SApple OSS Distributions 	if (os_thread_self_restrict_rwx_is_supported()) {
489*043036a2SApple OSS Distributions 		os_thread_self_restrict_rwx_to_rw();
490*043036a2SApple OSS Distributions 	}
491*043036a2SApple OSS Distributions 
492*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
493*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
494*043036a2SApple OSS Distributions 	}
495*043036a2SApple OSS Distributions 
496*043036a2SApple OSS Distributions 	/* Ensure that guard exceptions will not be fatal to the test process. */
497*043036a2SApple OSS Distributions 	enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY);
498*043036a2SApple OSS Distributions 
499*043036a2SApple OSS Distributions 	upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = true, .should_fail = true, .fault_prot = VM_PROT_NONE };
500*043036a2SApple OSS Distributions 
501*043036a2SApple OSS Distributions 	__block int64_t addr = (int64_t)&args;
502*043036a2SApple OSS Distributions 	__block int64_t result = 0;
503*043036a2SApple OSS Distributions 	__block size_t s = sizeof(result);
504*043036a2SApple OSS Distributions 	exc_guard_helper_info_t exc_info;
505*043036a2SApple OSS Distributions 	bool caught_exception =
506*043036a2SApple OSS Distributions 	    block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{
507*043036a2SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)),
508*043036a2SApple OSS Distributions 		"sysctlbyname(debug.test.vm_upl_object)");
509*043036a2SApple OSS Distributions 	});
510*043036a2SApple OSS Distributions 	if (args.should_fail) {
511*043036a2SApple OSS Distributions 		T_ASSERT_TRUE(caught_exception, "Failing test should also throw guard exception");
512*043036a2SApple OSS Distributions 		T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_IOPL_ON_EXEC_PAGE,
513*043036a2SApple OSS Distributions 		    "Failing test throws the expected guard exception flavor");
514*043036a2SApple OSS Distributions 		T_ASSERT_EQ(exc_info.catch_count, 1, "Failing test should throw exactly one guard exception");
515*043036a2SApple OSS Distributions 	} else {
516*043036a2SApple OSS Distributions 		T_ASSERT_FALSE(caught_exception, "Passing test should not throw guard exception");
517*043036a2SApple OSS Distributions 	}
518*043036a2SApple OSS Distributions 
519*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
520*043036a2SApple OSS Distributions }
521*043036a2SApple OSS Distributions 
522*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_with_exec_fault,
523*043036a2SApple OSS Distributions     "Attempt to exec-fault a region while a UPL is in-flight for that region")
524*043036a2SApple OSS Distributions {
525*043036a2SApple OSS Distributions 	/**
526*043036a2SApple OSS Distributions 	 * This test is meant to exercise functionality that is currently SPTM-specific.
527*043036a2SApple OSS Distributions 	 */
528*043036a2SApple OSS Distributions 	if (!is_sptm_enabled()) {
529*043036a2SApple OSS Distributions 		T_SKIP("Exec-fault test only supported on SPTM-enabled devices, skipping...");
530*043036a2SApple OSS Distributions 	}
531*043036a2SApple OSS Distributions 	if (process_is_translated()) {
532*043036a2SApple OSS Distributions 		/* TODO: Remove this once rdar://142438840 is fixed. */
533*043036a2SApple OSS Distributions 		T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping...");
534*043036a2SApple OSS Distributions 	}
535*043036a2SApple OSS Distributions 	if (!is_map_jit_allowed()) {
536*043036a2SApple OSS Distributions 		T_SKIP("MAP_JIT not allowed for this system configuration");
537*043036a2SApple OSS Distributions 	}
538*043036a2SApple OSS Distributions 
539*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
540*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);
541*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
542*043036a2SApple OSS Distributions 
543*043036a2SApple OSS Distributions 	if (os_thread_self_restrict_rwx_is_supported()) {
544*043036a2SApple OSS Distributions 		os_thread_self_restrict_rwx_to_rw();
545*043036a2SApple OSS Distributions 	}
546*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
547*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
548*043036a2SApple OSS Distributions 	}
549*043036a2SApple OSS Distributions 	if (os_thread_self_restrict_rwx_is_supported()) {
550*043036a2SApple OSS Distributions 		os_thread_self_restrict_rwx_to_rx();
551*043036a2SApple OSS Distributions 	}
552*043036a2SApple OSS Distributions 
553*043036a2SApple OSS Distributions 	/* Ensure that guard exceptions will not be fatal to the test process. */
554*043036a2SApple OSS Distributions 	enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY);
555*043036a2SApple OSS Distributions 
556*043036a2SApple OSS Distributions 	upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = false, .should_fail = false,
557*043036a2SApple OSS Distributions 		                      .fault_prot = VM_PROT_EXECUTE | VM_PROT_READ };
558*043036a2SApple OSS Distributions 
559*043036a2SApple OSS Distributions 	__block int64_t addr = (int64_t)&args;
560*043036a2SApple OSS Distributions 	__block int64_t result = 0;
561*043036a2SApple OSS Distributions 	__block size_t s = sizeof(result);
562*043036a2SApple OSS Distributions 	exc_guard_helper_info_t exc_info;
563*043036a2SApple OSS Distributions 	bool caught_exception =
564*043036a2SApple OSS Distributions 	    block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{
565*043036a2SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)),
566*043036a2SApple OSS Distributions 		"sysctlbyname(debug.test.vm_upl_object)");
567*043036a2SApple OSS Distributions 	});
568*043036a2SApple OSS Distributions 	T_ASSERT_TRUE(caught_exception, "Exec fault should throw guard exception");
569*043036a2SApple OSS Distributions 	T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_EXEC_ON_IOPL_PAGE,
570*043036a2SApple OSS Distributions 	    "Attempted exec fault throws the expected guard exception flavor");
571*043036a2SApple OSS Distributions 	T_ASSERT_EQ(exc_info.catch_count, 1, "Attempted exec fault should throw exactly one guard exception");
572*043036a2SApple OSS Distributions 
573*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
574*043036a2SApple OSS Distributions }
575*043036a2SApple OSS Distributions 
576*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_with_write_fault_on_exec_file,
577*043036a2SApple OSS Distributions     "Attempt to write-fault a file-backed executable region while a UPL is in-flight for that region")
578*043036a2SApple OSS Distributions {
579*043036a2SApple OSS Distributions #if TARGET_OS_OSX
580*043036a2SApple OSS Distributions 	/**
581*043036a2SApple OSS Distributions 	 * Test the bug uncovered in rdar://158063220.  This requires an on-the-fly retype to an
582*043036a2SApple OSS Distributions 	 * executable frame type in conjunction with a write fault on a file-backed page that is also
583*043036a2SApple OSS Distributions 	 * being cleaned in place.  This implies a "legacy JIT" mapping, since modern MAP_JIT mappings
584*043036a2SApple OSS Distributions 	 * aren't allowed for file-backed memory.  The existing vm_upl_object test hook can already
585*043036a2SApple OSS Distributions 	 * simulate the concurrent retype and in-place clean, so this test is mostly a matter of
586*043036a2SApple OSS Distributions 	 * setting up the file-backed memory correctly.
587*043036a2SApple OSS Distributions 	 */
588*043036a2SApple OSS Distributions 	/**
589*043036a2SApple OSS Distributions 	 * This test is meant to exercise functionality that is currently SPTM-specific.
590*043036a2SApple OSS Distributions 	 */
591*043036a2SApple OSS Distributions 	if (!is_sptm_enabled()) {
592*043036a2SApple OSS Distributions 		T_SKIP("Write-fault-on-exec test only supported on SPTM-enabled devices, skipping...");
593*043036a2SApple OSS Distributions 	}
594*043036a2SApple OSS Distributions 
595*043036a2SApple OSS Distributions 	if (process_is_translated()) {
596*043036a2SApple OSS Distributions 		/* TODO: Remove this once rdar://142438840 is fixed. */
597*043036a2SApple OSS Distributions 		T_SKIP("Guard exception handling does not work correctly with Rosetta (rdar://142438840), skipping...");
598*043036a2SApple OSS Distributions 	}
599*043036a2SApple OSS Distributions 
600*043036a2SApple OSS Distributions 	/* First, generate our backing file. */
601*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
602*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
603*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
604*043036a2SApple OSS Distributions 
605*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
606*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
607*043036a2SApple OSS Distributions 	}
608*043036a2SApple OSS Distributions 
609*043036a2SApple OSS Distributions 	ssize_t nbytes;
610*043036a2SApple OSS Distributions 	int fd;
611*043036a2SApple OSS Distributions 	char tmp_file_name[PATH_MAX] = "/tmp/vm_upl_data.XXXXXXXX";
612*043036a2SApple OSS Distributions 	T_ASSERT_NOTNULL(mktemp(tmp_file_name), "create temporary file name");
613*043036a2SApple OSS Distributions 	T_WITH_ERRNO; T_QUIET; T_ASSERT_GE(fd = open(tmp_file_name, O_CREAT | O_RDWR),
614*043036a2SApple OSS Distributions 	    0, "create temp file");
615*043036a2SApple OSS Distributions 	T_WITH_ERRNO; T_QUIET; T_ASSERT_EQ(nbytes = write(fd, buf, buf_size),
616*043036a2SApple OSS Distributions 	    (ssize_t)buf_size, "write %zu bytes", buf_size);
617*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
618*043036a2SApple OSS Distributions 
619*043036a2SApple OSS Distributions 	/* Map the backing file. */
620*043036a2SApple OSS Distributions 	buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
621*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
622*043036a2SApple OSS Distributions 
623*043036a2SApple OSS Distributions 	/**
624*043036a2SApple OSS Distributions 	 * Twiddle the mapping permissions between RX and RW.  This will mark the mapping as "user debug",
625*043036a2SApple OSS Distributions 	 * which will induce a retype when the backing pages are faulted in.
626*043036a2SApple OSS Distributions 	 */
627*043036a2SApple OSS Distributions 	kern_return_t kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)buf, buf_size, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
628*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_protect(RX)");
629*043036a2SApple OSS Distributions 
630*043036a2SApple OSS Distributions 	kr = mach_vm_protect(mach_task_self(), (mach_vm_address_t)buf, buf_size, FALSE, VM_PROT_READ | VM_PROT_WRITE);
631*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_vm_protect(RW)");
632*043036a2SApple OSS Distributions 
633*043036a2SApple OSS Distributions 	/* Ensure that guard exceptions will not be fatal to the test process. */
634*043036a2SApple OSS Distributions 	enable_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY);
635*043036a2SApple OSS Distributions 
636*043036a2SApple OSS Distributions 	upl_object_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_rw = false, .should_fail = false,
637*043036a2SApple OSS Distributions 		                      .fault_prot = VM_PROT_WRITE | VM_PROT_READ };
638*043036a2SApple OSS Distributions 
639*043036a2SApple OSS Distributions 	__block int64_t addr = (int64_t)&args;
640*043036a2SApple OSS Distributions 	__block int64_t result = 0;
641*043036a2SApple OSS Distributions 	__block size_t s = sizeof(result);
642*043036a2SApple OSS Distributions 	exc_guard_helper_info_t exc_info;
643*043036a2SApple OSS Distributions 	bool caught_exception =
644*043036a2SApple OSS Distributions 	    block_raised_exc_guard_of_type(GUARD_TYPE_VIRT_MEMORY, &exc_info, ^{
645*043036a2SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_object", &result, &s, &addr, sizeof(addr)),
646*043036a2SApple OSS Distributions 		"sysctlbyname(debug.test.vm_upl_object)");
647*043036a2SApple OSS Distributions 	});
648*043036a2SApple OSS Distributions 	if (!process_is_translated()) {
649*043036a2SApple OSS Distributions 		T_ASSERT_TRUE(caught_exception, "Exec fault should throw guard exception");
650*043036a2SApple OSS Distributions 		T_ASSERT_EQ(exc_info.guard_flavor, kGUARD_EXC_SEC_EXEC_ON_IOPL_PAGE,
651*043036a2SApple OSS Distributions 		    "Attempted exec fault throws the expected guard exception flavor");
652*043036a2SApple OSS Distributions 		T_ASSERT_EQ(exc_info.catch_count, 1, "Attempted exec fault should throw exactly one guard exception");
653*043036a2SApple OSS Distributions 	}
654*043036a2SApple OSS Distributions 
655*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
656*043036a2SApple OSS Distributions 	close(fd);
657*043036a2SApple OSS Distributions #else
658*043036a2SApple OSS Distributions 	T_SKIP("Write-fault on exec file requires non-MAP_JIT RWX support");
659*043036a2SApple OSS Distributions #endif /* TARGET_OS_OSX */
660*043036a2SApple OSS Distributions }
661*043036a2SApple OSS Distributions 
662*043036a2SApple OSS Distributions typedef struct {
663*043036a2SApple OSS Distributions 	uint64_t ptr;
664*043036a2SApple OSS Distributions 	uint64_t upl_base;
665*043036a2SApple OSS Distributions 	uint32_t size;
666*043036a2SApple OSS Distributions 	uint32_t upl_size;
667*043036a2SApple OSS Distributions 	bool upl_rw;
668*043036a2SApple OSS Distributions } upl_submap_test_args;
669*043036a2SApple OSS Distributions 
670*043036a2SApple OSS Distributions T_DECL(vm_upl_ro_on_submap,
671*043036a2SApple OSS Distributions     "Generate RO UPL against a submap region")
672*043036a2SApple OSS Distributions {
673*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
674*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
675*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
676*043036a2SApple OSS Distributions 
677*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
678*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
679*043036a2SApple OSS Distributions 	}
680*043036a2SApple OSS Distributions 
681*043036a2SApple OSS Distributions 	upl_submap_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_base = 0x180000000ULL,
682*043036a2SApple OSS Distributions 		                      .upl_size = buf_size, .upl_rw = false };
683*043036a2SApple OSS Distributions 
684*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
685*043036a2SApple OSS Distributions 	int64_t result = 0;
686*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
687*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
688*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap)");
689*043036a2SApple OSS Distributions 
690*043036a2SApple OSS Distributions 	args.upl_base += 0x800;
691*043036a2SApple OSS Distributions 	args.upl_size -= 0x1000;
692*043036a2SApple OSS Distributions 
693*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
694*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap)");
695*043036a2SApple OSS Distributions 
696*043036a2SApple OSS Distributions 	args.upl_base += 0x800;
697*043036a2SApple OSS Distributions 	args.upl_size -= 0x1000;
698*043036a2SApple OSS Distributions 
699*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
700*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap)");
701*043036a2SApple OSS Distributions 
702*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
703*043036a2SApple OSS Distributions }
704*043036a2SApple OSS Distributions 
705*043036a2SApple OSS Distributions T_DECL(vm_upl_rw_on_submap,
706*043036a2SApple OSS Distributions     "Generate RW UPL against a submap region")
707*043036a2SApple OSS Distributions {
708*043036a2SApple OSS Distributions 	const size_t buf_size = 10 * PAGE_SIZE;
709*043036a2SApple OSS Distributions 	unsigned int *buf = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
710*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NE_PTR(buf, MAP_FAILED, "map buffer");
711*043036a2SApple OSS Distributions 
712*043036a2SApple OSS Distributions 	for (unsigned int i = 0; i < (buf_size / sizeof(*buf)); i++) {
713*043036a2SApple OSS Distributions 		buf[i] = (unsigned int)'a' + i;
714*043036a2SApple OSS Distributions 	}
715*043036a2SApple OSS Distributions 
716*043036a2SApple OSS Distributions 	upl_submap_test_args args = { .ptr = (uint64_t)buf, .size = buf_size, .upl_base = 0x180000000ULL,
717*043036a2SApple OSS Distributions 		                      .upl_size = buf_size, .upl_rw = true };
718*043036a2SApple OSS Distributions 
719*043036a2SApple OSS Distributions 	int64_t addr = (int64_t)&args;
720*043036a2SApple OSS Distributions 	int64_t result = 0;
721*043036a2SApple OSS Distributions 	size_t s = sizeof(result);
722*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
723*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap)");
724*043036a2SApple OSS Distributions 
725*043036a2SApple OSS Distributions 	args.upl_base += 0x800;
726*043036a2SApple OSS Distributions 	args.upl_size -= 0x1000;
727*043036a2SApple OSS Distributions 
728*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
729*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap");
730*043036a2SApple OSS Distributions 
731*043036a2SApple OSS Distributions 	args.upl_base += 0x800;
732*043036a2SApple OSS Distributions 	args.upl_size -= 0x1000;
733*043036a2SApple OSS Distributions 
734*043036a2SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("debug.test.vm_upl_submap", &result, &s, &addr, sizeof(addr)),
735*043036a2SApple OSS Distributions 	    "sysctlbyname(debug.test.vm_upl_submap)");
736*043036a2SApple OSS Distributions 
737*043036a2SApple OSS Distributions 	munmap(buf, buf_size);
738*043036a2SApple OSS Distributions }
739