xref: /xnu-12377.41.6/bsd/tests/bsd_tests.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828) !
1 /*
2  * Copyright (c) 2019-2021 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 <tests/ktest.h>
30 #include <tests/xnupost.h>
31 #include <kern/assert.h>
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/types.h>
35 #include <sys/kdebug.h>
36 #include <libkern/libkern.h>
37 #include <kern/kalloc.h>
38 #include <sys/cdefs.h>
39 #include <libkern/version.h>
40 #include <kern/clock.h>
41 #include <kern/kern_cdata.h>
42 #include <pexpert/pexpert.h>
43 
44 
45 #if !(DEVELOPMENT || DEBUG)
46 #error "Testing is not enabled on RELEASE configurations"
47 #endif
48 
49 #if defined(__arm64__)
50 extern kern_return_t arm64_lock_test(void);
51 extern kern_return_t arm_cpu_capabilities_legacy_test(void);
52 extern kern_return_t pmap_test(void);
53 #endif /* defined(__arm64__) */
54 kern_return_t ipi_test(void);
55 #if defined(KERNEL_INTEGRITY_CTRR) || defined(KERNEL_INTEGRITY_PV_CTRR)
56 extern kern_return_t ctrr_test(void);
57 #endif
58 #if __ARM_PAN_AVAILABLE__
59 extern kern_return_t arm64_late_pan_test(void);
60 #endif
61 extern kern_return_t copyio_test(void);
62 #if HAS_MTE
63 extern kern_return_t copyio_unprivileged_test(void);
64 #endif
65 extern kern_return_t parse_boot_arg_test(void);
66 
67 struct xnupost_test bsd_post_tests[] = {
68 #ifdef __arm64__
69 	XNUPOST_TEST_CONFIG_BASIC(arm64_lock_test),
70 #endif
71 #if defined(__arm64__)
72 	XNUPOST_TEST_CONFIG_BASIC(arm_cpu_capabilities_legacy_test),
73 	XNUPOST_TEST_CONFIG_BASIC(pmap_test),
74 #endif /* defined(__arm64__) */
75 #if CONFIG_SPTM
76 /**
77  * SPTM TODO: The CTRR test is currently not functional in SPTM systems.
78  *            This will be addressed in a future change.
79  */
80 #else
81 #if defined(KERNEL_INTEGRITY_CTRR) || defined(KERNEL_INTEGRITY_PV_CTRR)
82 	XNUPOST_TEST_CONFIG_BASIC(ctrr_test),
83 #endif
84 #endif
85 #if __ARM_PAN_AVAILABLE__
86 	XNUPOST_TEST_CONFIG_BASIC(arm64_late_pan_test),
87 #endif
88 	XNUPOST_TEST_CONFIG_BASIC(ipi_test),
89 	XNUPOST_TEST_CONFIG_BASIC(copyio_test),
90 #if HAS_MTE
91 	XNUPOST_TEST_CONFIG_BASIC(copyio_unprivileged_test),
92 #endif
93 	XNUPOST_TEST_CONFIG_BASIC(parse_boot_arg_test),
94 };
95 
96 uint32_t bsd_post_tests_count = sizeof(bsd_post_tests) / sizeof(xnupost_test_data_t);
97 
98 extern uint64_t last_loaded_timestamp; /* updated by OSKext::load() */
99 extern uint64_t kernel_post_args;
100 int
bsd_list_tests()101 bsd_list_tests()
102 {
103 	if (kernel_post_args == 0) {
104 		return 0;
105 	}
106 
107 	uint64_t prev_load_time    = last_loaded_timestamp;
108 	int no_load_counter        = 5;
109 	int absolute_break_counter = 15;
110 	int delay_duration_usecs   = 300000; /* 0.3 second for kext loading to stabilize */
111 
112 	while (no_load_counter > 0) {
113 		printf("bsd_list_tests:INFO waiting for %d usecs\n", delay_duration_usecs);
114 		printf("bsd_list_tests: prev: %llu current: %llu\n", prev_load_time, last_loaded_timestamp);
115 
116 		delay(delay_duration_usecs);
117 		absolute_break_counter -= 1;
118 
119 		if (absolute_break_counter <= 0) {
120 			printf("bsd_list_tests: WARNING: Waiting beyond normal time for stabilizing kext loading\n");
121 			break;
122 		}
123 
124 		if (prev_load_time == last_loaded_timestamp) {
125 			no_load_counter -= 1;
126 			printf("bsd_list_tests: INFO: no new kexts loaded. remaining checks: %d\n", no_load_counter);
127 		}
128 
129 		prev_load_time = last_loaded_timestamp;
130 	}
131 
132 	return xnupost_list_tests(bsd_post_tests, bsd_post_tests_count);
133 }
134 
135 int
bsd_do_post(void)136 bsd_do_post(void)
137 {
138 	return xnupost_run_tests(bsd_post_tests, bsd_post_tests_count);
139 }
140 
141 /* kcdata type definition */
142 #define XNUPOST_TNAME_MAXLEN 132
143 
144 struct kcdata_subtype_descriptor kc_xnupost_test_def[] = {
145 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT16, .kcs_elem_offset = 0, .kcs_elem_size = sizeof(uint16_t), .kcs_name = "config"},
146 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT16, .kcs_elem_offset = 1 * sizeof(uint16_t), .kcs_elem_size = sizeof(uint16_t), .kcs_name = "test_num"},
147 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_INT32, .kcs_elem_offset = 2 * sizeof(uint16_t), .kcs_elem_size = sizeof(int32_t), .kcs_name = "retval"},
148 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_INT32, .kcs_elem_offset = 2 * sizeof(uint16_t) + sizeof(int32_t), .kcs_elem_size = sizeof(int32_t), .kcs_name = "expected_retval"},
149 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT64, .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t)), .kcs_elem_size = sizeof(uint64_t), .kcs_name = "begin_time"},
150 	{.kcs_flags = KCS_SUBTYPE_FLAGS_NONE, .kcs_elem_type = KC_ST_UINT64, .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t)) + sizeof(uint64_t), .kcs_elem_size = sizeof(uint64_t), .kcs_name = "end_time"},
151 	{.kcs_flags = KCS_SUBTYPE_FLAGS_ARRAY,
152 	 .kcs_elem_type = KC_ST_CHAR,
153 	 .kcs_elem_offset = 2 * (sizeof(uint16_t) + sizeof(int32_t) + sizeof(uint64_t)),
154 	 .kcs_elem_size = KCS_SUBTYPE_PACK_SIZE(XNUPOST_TNAME_MAXLEN * sizeof(char), sizeof(char)),
155 	 .kcs_name = "test_name"}
156 };
157 
158 const uint32_t kc_xnupost_test_def_count = sizeof(kc_xnupost_test_def) / sizeof(struct kcdata_subtype_descriptor);
159 
160 kern_return_t xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr);
161 
162 int
xnupost_copyout_test(xnupost_test_t t,mach_vm_address_t outaddr)163 xnupost_copyout_test(xnupost_test_t t, mach_vm_address_t outaddr)
164 {
165 	/* code to copyout test config */
166 	int kret         = 0;
167 	size_t namelen = 0;
168 
169 	kret = copyout(&t->xt_config, (user_addr_t)outaddr, sizeof(uint16_t));
170 	if (kret) {
171 		return kret;
172 	}
173 	outaddr += sizeof(uint16_t);
174 
175 	kret = copyout(&t->xt_test_num, (user_addr_t)outaddr, sizeof(uint16_t));
176 	if (kret) {
177 		return kret;
178 	}
179 	outaddr += sizeof(uint16_t);
180 
181 	kret = copyout(&t->xt_retval, (user_addr_t)outaddr, sizeof(uint32_t));
182 	if (kret) {
183 		return kret;
184 	}
185 	outaddr += sizeof(uint32_t);
186 
187 	kret = copyout(&t->xt_expected_retval, (user_addr_t)outaddr, sizeof(uint32_t));
188 	if (kret) {
189 		return kret;
190 	}
191 	outaddr += sizeof(uint32_t);
192 
193 	kret = copyout(&t->xt_begin_time, (user_addr_t)outaddr, sizeof(uint64_t));
194 	if (kret) {
195 		return kret;
196 	}
197 	outaddr += sizeof(uint64_t);
198 
199 	kret = copyout(&t->xt_end_time, (user_addr_t)outaddr, sizeof(uint64_t));
200 	if (kret) {
201 		return kret;
202 	}
203 	outaddr += sizeof(uint64_t);
204 
205 	namelen = strnlen(t->xt_name, XNUPOST_TNAME_MAXLEN - 1) + 1;
206 	kret = copyout(t->xt_name, (user_addr_t)outaddr, namelen);
207 	if (kret) {
208 		return kret;
209 	}
210 	outaddr += namelen;
211 
212 	return 0;
213 }
214 
215 uint32_t
xnupost_get_estimated_testdata_size(void)216 xnupost_get_estimated_testdata_size(void)
217 {
218 	uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count;
219 	uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset +
220 	    kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]);
221 	uint32_t retval = 1024; /* account for type definition and mach timebase */
222 	retval += 1024;         /* kernel version and boot-args string data */
223 	retval += (total_tests * elem_size);
224 
225 	return retval;
226 }
227 
228 int
xnupost_export_testdata(void * outp,size_t size_in,uint32_t * lenp)229 xnupost_export_testdata(void * outp, size_t size_in, uint32_t * lenp)
230 {
231 	struct kcdata_descriptor kcd;
232 	mach_vm_address_t user_addr        = 0;
233 	mach_vm_address_t tmp_entry_addr   = 0;
234 	kern_return_t kret                 = 0;
235 	uint32_t i                         = 0;
236 	char kctype_name[32]               = "xnupost_test_config";
237 	mach_timebase_info_data_t timebase = {0, 0};
238 	uint32_t length_to_copy            = 0;
239 	unsigned int size                  = (unsigned int)size_in;
240 
241 	if (size_in > UINT_MAX) {
242 		return ENOSPC;
243 	}
244 
245 #define RET_IF_OP_FAIL                                                                                       \
246 	do {                                                                                                     \
247 	        if (kret != KERN_SUCCESS) {                                                                          \
248 	                return (kret == KERN_NO_ACCESS) ? EACCES : ((kret == KERN_RESOURCE_SHORTAGE) ? ENOMEM : EINVAL); \
249 	        }                                                                                                    \
250 	} while (0)
251 
252 	kret = kcdata_memory_static_init(&kcd, (mach_vm_address_t)outp, KCDATA_BUFFER_BEGIN_XNUPOST_CONFIG, size, KCFLAG_USE_COPYOUT);
253 	RET_IF_OP_FAIL;
254 
255 	/* add mach timebase info */
256 	clock_timebase_info(&timebase);
257 	kret = kcdata_get_memory_addr(&kcd, KCDATA_TYPE_TIMEBASE, sizeof(timebase), &user_addr);
258 	RET_IF_OP_FAIL;
259 	kret = copyout(&timebase, (user_addr_t)user_addr, sizeof(timebase));
260 	RET_IF_OP_FAIL;
261 
262 	/* save boot-args and osversion string */
263 	length_to_copy = MIN((uint32_t)(strlen(version) + 1), OSVERSIZE);
264 	kret           = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_OSVERSION, length_to_copy, &user_addr);
265 	RET_IF_OP_FAIL;
266 	kret = copyout(&version[0], (user_addr_t)user_addr, length_to_copy);
267 	RET_IF_OP_FAIL;
268 
269 	length_to_copy = MIN((uint32_t)(strlen(PE_boot_args()) + 1), BOOT_LINE_LENGTH);
270 	kret           = kcdata_get_memory_addr(&kcd, STACKSHOT_KCTYPE_BOOTARGS, length_to_copy, &user_addr);
271 	RET_IF_OP_FAIL;
272 	kret = copyout(PE_boot_args(), (user_addr_t)user_addr, length_to_copy);
273 	RET_IF_OP_FAIL;
274 
275 	/* add type definition to buffer */
276 	kret = kcdata_add_type_definition(&kcd, XNUPOST_KCTYPE_TESTCONFIG, kctype_name, &kc_xnupost_test_def[0],
277 	    kc_xnupost_test_def_count);
278 	RET_IF_OP_FAIL;
279 
280 	/* add the tests to buffer as array */
281 	uint32_t total_tests = bsd_post_tests_count + kernel_post_tests_count;
282 	uint32_t elem_size = kc_xnupost_test_def[kc_xnupost_test_def_count - 1].kcs_elem_offset +
283 	    kcs_get_elem_size(&kc_xnupost_test_def[kc_xnupost_test_def_count - 1]);
284 
285 	kret = kcdata_get_memory_addr_for_array(&kcd, XNUPOST_KCTYPE_TESTCONFIG, elem_size, total_tests, &user_addr);
286 	RET_IF_OP_FAIL;
287 
288 	for (i = 0; i < bsd_post_tests_count; i++) {
289 		tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
290 		kret           = xnupost_copyout_test(&bsd_post_tests[i], tmp_entry_addr);
291 		RET_IF_OP_FAIL;
292 	}
293 	user_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
294 
295 	for (i = 0; i < kernel_post_tests_count; i++) {
296 		tmp_entry_addr = (mach_vm_address_t)((uint64_t)(user_addr) + (uint64_t)(i * elem_size));
297 		kret           = xnupost_copyout_test(&kernel_post_tests[i], tmp_entry_addr);
298 		RET_IF_OP_FAIL;
299 	}
300 
301 	if (kret == KERN_SUCCESS && lenp != NULL) {
302 		*lenp = (uint32_t)kcdata_memory_get_used_bytes(&kcd);
303 	}
304 	RET_IF_OP_FAIL;
305 
306 #undef RET_IF_OP_FAIL
307 	return kret;
308 }
309 
310 int
xnupost_reset_all_tests(void)311 xnupost_reset_all_tests(void)
312 {
313 	xnupost_reset_tests(&bsd_post_tests[0], bsd_post_tests_count);
314 	xnupost_reset_tests(&kernel_post_tests[0], kernel_post_tests_count);
315 	return 0;
316 }
317