xref: /xnu-10063.101.15/tests/kas_info.c (revision 94d3b452840153a99b38a3a9659680b2a006908e)
1*94d3b452SApple OSS Distributions /* Copyright (c) 2020 Apple Computer, Inc.  All rights reserved. */
2*94d3b452SApple OSS Distributions 
3*94d3b452SApple OSS Distributions #include <CoreSymbolication/CoreSymbolication.h>
4*94d3b452SApple OSS Distributions #include <CoreSymbolication/CoreSymbolicationPrivate.h>
5*94d3b452SApple OSS Distributions #include <darwintest.h>
6*94d3b452SApple OSS Distributions #include <dispatch/dispatch.h>
7*94d3b452SApple OSS Distributions 
8*94d3b452SApple OSS Distributions #include <mach-o/loader.h>
9*94d3b452SApple OSS Distributions 
10*94d3b452SApple OSS Distributions #include <sys/kas_info.h>
11*94d3b452SApple OSS Distributions 
12*94d3b452SApple OSS Distributions #include <sys/mman.h>
13*94d3b452SApple OSS Distributions #include <sys/stat.h>
14*94d3b452SApple OSS Distributions #include <sys/sysctl.h>
15*94d3b452SApple OSS Distributions #include <sys/types.h>
16*94d3b452SApple OSS Distributions 
17*94d3b452SApple OSS Distributions #include <fcntl.h>
18*94d3b452SApple OSS Distributions 
19*94d3b452SApple OSS Distributions #include <stdint.h>
20*94d3b452SApple OSS Distributions 
21*94d3b452SApple OSS Distributions T_GLOBAL_META(
22*94d3b452SApple OSS Distributions 	T_META_NAMESPACE("xnu.kas_info"),
23*94d3b452SApple OSS Distributions 	T_META_CHECK_LEAKS(false),
24*94d3b452SApple OSS Distributions 	T_META_ASROOT(true));
25*94d3b452SApple OSS Distributions 
26*94d3b452SApple OSS Distributions static bool
slide_enabled(void)27*94d3b452SApple OSS Distributions slide_enabled(void)
28*94d3b452SApple OSS Distributions {
29*94d3b452SApple OSS Distributions 	int slide_enabled, err;
30*94d3b452SApple OSS Distributions 	size_t size = sizeof(slide_enabled);
31*94d3b452SApple OSS Distributions 	err = sysctlbyname("kern.slide", &slide_enabled, &size, NULL, 0);
32*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "sysctl(\"kern.slide\");");
33*94d3b452SApple OSS Distributions 	return slide_enabled != 0;
34*94d3b452SApple OSS Distributions }
35*94d3b452SApple OSS Distributions 
36*94d3b452SApple OSS Distributions static uint64_t
kernel_slide(int selector)37*94d3b452SApple OSS Distributions kernel_slide(int selector)
38*94d3b452SApple OSS Distributions {
39*94d3b452SApple OSS Distributions 	uint64_t slide;
40*94d3b452SApple OSS Distributions 	size_t size = sizeof(slide);
41*94d3b452SApple OSS Distributions 	int err = kas_info(selector, &slide, &size);
42*94d3b452SApple OSS Distributions 	if (err && errno == ENOTSUP) {
43*94d3b452SApple OSS Distributions 		T_SKIP("Running on kernel without kas_info");
44*94d3b452SApple OSS Distributions 	}
45*94d3b452SApple OSS Distributions 
46*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(errno, "kas_info with selector %d", selector);
47*94d3b452SApple OSS Distributions 	T_ASSERT_EQ(size, sizeof(slide), "returned size is valid");
48*94d3b452SApple OSS Distributions 
49*94d3b452SApple OSS Distributions 	return slide;
50*94d3b452SApple OSS Distributions }
51*94d3b452SApple OSS Distributions 
52*94d3b452SApple OSS Distributions T_DECL(kernel_text_slide,
53*94d3b452SApple OSS Distributions     "ensures that kas_info can return the kernel text slide")
54*94d3b452SApple OSS Distributions {
55*94d3b452SApple OSS Distributions 	if (!slide_enabled()) {
56*94d3b452SApple OSS Distributions 		T_SKIP("KASLR is not enabled");
57*94d3b452SApple OSS Distributions 		__builtin_unreachable();
58*94d3b452SApple OSS Distributions 	}
59*94d3b452SApple OSS Distributions 
60*94d3b452SApple OSS Distributions 	uint64_t slide = kernel_slide(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR);
61*94d3b452SApple OSS Distributions 
62*94d3b452SApple OSS Distributions 	T_ASSERT_GT_ULLONG(slide, 0ULL, "kernel slide is non-zero");
63*94d3b452SApple OSS Distributions }
64*94d3b452SApple OSS Distributions 
65*94d3b452SApple OSS Distributions static void
test_kas_info_invalid_args(int selector)66*94d3b452SApple OSS Distributions test_kas_info_invalid_args(int selector)
67*94d3b452SApple OSS Distributions {
68*94d3b452SApple OSS Distributions 	uint64_t slide;
69*94d3b452SApple OSS Distributions 	size_t size = 0;
70*94d3b452SApple OSS Distributions 	int err;
71*94d3b452SApple OSS Distributions 
72*94d3b452SApple OSS Distributions 	err = kas_info(selector, &slide, NULL);
73*94d3b452SApple OSS Distributions 	if (errno == ENOTSUP) {
74*94d3b452SApple OSS Distributions 		T_SKIP("Running on kernel without kas_info %d", selector);
75*94d3b452SApple OSS Distributions 	}
76*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_FAILURE(err, EFAULT, "kas_info %d with NULL size", selector);
77*94d3b452SApple OSS Distributions 
78*94d3b452SApple OSS Distributions 	size = sizeof(uint64_t);
79*94d3b452SApple OSS Distributions 	err = kas_info(selector, NULL, &size);
80*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_FAILURE(err, EFAULT, "kas_info %d with NULL slide", selector);
81*94d3b452SApple OSS Distributions 
82*94d3b452SApple OSS Distributions 	size = sizeof(uint32_t);
83*94d3b452SApple OSS Distributions 	err = kas_info(selector, &slide, &size);
84*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_FAILURE(err, EINVAL, "kas_info %d with invalid size", selector);
85*94d3b452SApple OSS Distributions }
86*94d3b452SApple OSS Distributions 
87*94d3b452SApple OSS Distributions T_DECL(kernel_text_slide_invalid,
88*94d3b452SApple OSS Distributions     "ensures that kas_info handles invalid input to KERNEL_TEXT_SLIDE_SELECTOR")
89*94d3b452SApple OSS Distributions {
90*94d3b452SApple OSS Distributions 	test_kas_info_invalid_args(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR);
91*94d3b452SApple OSS Distributions }
92*94d3b452SApple OSS Distributions 
93*94d3b452SApple OSS Distributions static bool
sptm_enabled(void)94*94d3b452SApple OSS Distributions sptm_enabled(void)
95*94d3b452SApple OSS Distributions {
96*94d3b452SApple OSS Distributions 	int page_protection_type, err;
97*94d3b452SApple OSS Distributions 	size_t size = sizeof(page_protection_type);
98*94d3b452SApple OSS Distributions 	err = sysctlbyname("kern.page_protection_type", &page_protection_type, &size, NULL, 0);
99*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "sysctl(\"kern.page_protection_type\");");
100*94d3b452SApple OSS Distributions 	return page_protection_type == 2;
101*94d3b452SApple OSS Distributions }
102*94d3b452SApple OSS Distributions 
103*94d3b452SApple OSS Distributions T_DECL(sptm_txm_text_slide,
104*94d3b452SApple OSS Distributions     "ensures that kas_info can return the SPTM/TXM text slides")
105*94d3b452SApple OSS Distributions {
106*94d3b452SApple OSS Distributions 	const uint64_t sptm_slide = kernel_slide(KAS_INFO_SPTM_TEXT_SLIDE_SELECTOR);
107*94d3b452SApple OSS Distributions 	const uint64_t txm_slide = kernel_slide(KAS_INFO_TXM_TEXT_SLIDE_SELECTOR);
108*94d3b452SApple OSS Distributions 
109*94d3b452SApple OSS Distributions 	if (sptm_enabled()) {
110*94d3b452SApple OSS Distributions 		T_ASSERT_GT_ULLONG(sptm_slide, 0ULL, "Ensure the SPTM slide is non-zero on an SPTM-enabled system");
111*94d3b452SApple OSS Distributions 		T_ASSERT_GT_ULLONG(txm_slide, 0ULL, "Ensure the TXM slide is non-zero on an SPTM-enabled system");
112*94d3b452SApple OSS Distributions 	} else {
113*94d3b452SApple OSS Distributions 		/* When not running on an SPTM-enabled system, this selector should return zero. */
114*94d3b452SApple OSS Distributions 		T_ASSERT_EQ(sptm_slide, 0ULL, "Ensure the SPTM slide is zero on a non-SPTM system");
115*94d3b452SApple OSS Distributions 		T_ASSERT_EQ(txm_slide, 0ULL, "Ensure the TXM slide is zero on a non-SPTM system");
116*94d3b452SApple OSS Distributions 	}
117*94d3b452SApple OSS Distributions }
118*94d3b452SApple OSS Distributions 
119*94d3b452SApple OSS Distributions T_DECL(sptm_txm_text_slide_invalid,
120*94d3b452SApple OSS Distributions     "ensures that kas_info handles invalid input to [SPTM|TXM]_TEXT_SLIDE_SELECTOR")
121*94d3b452SApple OSS Distributions {
122*94d3b452SApple OSS Distributions 	test_kas_info_invalid_args(KAS_INFO_SPTM_TEXT_SLIDE_SELECTOR);
123*94d3b452SApple OSS Distributions 	test_kas_info_invalid_args(KAS_INFO_TXM_TEXT_SLIDE_SELECTOR);
124*94d3b452SApple OSS Distributions }
125*94d3b452SApple OSS Distributions 
126*94d3b452SApple OSS Distributions static char const*
kernel_path(void)127*94d3b452SApple OSS Distributions kernel_path(void)
128*94d3b452SApple OSS Distributions {
129*94d3b452SApple OSS Distributions 	static CSSymbolicatorRef symbolicator;
130*94d3b452SApple OSS Distributions 	static char const* path;
131*94d3b452SApple OSS Distributions 	static dispatch_once_t once;
132*94d3b452SApple OSS Distributions 	dispatch_once(&once, ^{
133*94d3b452SApple OSS Distributions 		uint32_t flags = kCSSymbolicatorDefaultCreateFlags;
134*94d3b452SApple OSS Distributions 		symbolicator = CSSymbolicatorCreateWithMachKernelFlagsAndNotification(flags, NULL);
135*94d3b452SApple OSS Distributions 		T_QUIET; T_ASSERT_TRUE(!CSIsNull(symbolicator), "CSSymbolicatorCreateWithMachKernelFlagsAndNotification");
136*94d3b452SApple OSS Distributions 		path = CSSymbolOwnerGetPath(CSSymbolicatorGetAOutSymbolOwner(symbolicator));
137*94d3b452SApple OSS Distributions 		if (!path) {
138*94d3b452SApple OSS Distributions 		        path = CSSymbolOwnerGetPath(CSSymbolicatorGetSymbolOwner(symbolicator));
139*94d3b452SApple OSS Distributions 		}
140*94d3b452SApple OSS Distributions 		T_QUIET; T_ASSERT_NOTNULL(path, "CSSymbolOwnerGetPath/CSSymbolicatorGetSymbolOwner");
141*94d3b452SApple OSS Distributions 	});
142*94d3b452SApple OSS Distributions 	return path;
143*94d3b452SApple OSS Distributions }
144*94d3b452SApple OSS Distributions 
145*94d3b452SApple OSS Distributions static void
disk_kernel_segments(uint64_t ** segs_out,size_t * nsegs_out)146*94d3b452SApple OSS Distributions disk_kernel_segments(uint64_t **segs_out, size_t *nsegs_out)
147*94d3b452SApple OSS Distributions {
148*94d3b452SApple OSS Distributions 	char const* path = kernel_path();
149*94d3b452SApple OSS Distributions 	int fd = open(path, O_RDONLY);
150*94d3b452SApple OSS Distributions 	int err;
151*94d3b452SApple OSS Distributions 	struct stat sb;
152*94d3b452SApple OSS Distributions 	size_t nsegs = 0;
153*94d3b452SApple OSS Distributions 	uint64_t *segs = NULL;
154*94d3b452SApple OSS Distributions 	void *data;
155*94d3b452SApple OSS Distributions 
156*94d3b452SApple OSS Distributions 	T_LOG("Kernel file is %s", path);
157*94d3b452SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(fd, "open kernel file");
158*94d3b452SApple OSS Distributions 
159*94d3b452SApple OSS Distributions 	err = fstat(fd, &sb);
160*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "fstat kernel file");
161*94d3b452SApple OSS Distributions 
162*94d3b452SApple OSS Distributions 	data = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
163*94d3b452SApple OSS Distributions 	T_ASSERT_NE(data, MAP_FAILED, "mmap kernel file");
164*94d3b452SApple OSS Distributions 
165*94d3b452SApple OSS Distributions 	/*
166*94d3b452SApple OSS Distributions 	 * TODO: If we bring back FAT kernel binaries
167*94d3b452SApple OSS Distributions 	 * this will need to be fixed to handle them properly
168*94d3b452SApple OSS Distributions 	 */
169*94d3b452SApple OSS Distributions 	uint32_t magic = *(uint32_t*)data;
170*94d3b452SApple OSS Distributions 	struct load_command *cmd = NULL;
171*94d3b452SApple OSS Distributions 
172*94d3b452SApple OSS Distributions 	switch (magic) {
173*94d3b452SApple OSS Distributions 	case MH_MAGIC: OS_FALLTHROUGH;
174*94d3b452SApple OSS Distributions 	case MH_CIGAM: {
175*94d3b452SApple OSS Distributions 		struct mach_header *mh = (struct mach_header *)data;
176*94d3b452SApple OSS Distributions 		cmd = (struct load_command *)(&(mh[1]));
177*94d3b452SApple OSS Distributions 		nsegs = mh->ncmds;
178*94d3b452SApple OSS Distributions 	}
179*94d3b452SApple OSS Distributions 	break;
180*94d3b452SApple OSS Distributions 	case MH_MAGIC_64: OS_FALLTHROUGH;
181*94d3b452SApple OSS Distributions 	case MH_CIGAM_64: {
182*94d3b452SApple OSS Distributions 		struct mach_header_64 *mh = (struct mach_header_64 *)data;
183*94d3b452SApple OSS Distributions 		cmd = (struct load_command *)(&(mh[1]));
184*94d3b452SApple OSS Distributions 		nsegs = mh->ncmds;
185*94d3b452SApple OSS Distributions 	}
186*94d3b452SApple OSS Distributions 	break;
187*94d3b452SApple OSS Distributions 	default:
188*94d3b452SApple OSS Distributions 		T_FAIL("kernel file is not a Mach-O file, magic is %x", magic);
189*94d3b452SApple OSS Distributions 	}
190*94d3b452SApple OSS Distributions 
191*94d3b452SApple OSS Distributions 	/* Adjust for the LC_UUID && LC_BUILD_VERSION commands in front of
192*94d3b452SApple OSS Distributions 	 * load commands for dSYMs
193*94d3b452SApple OSS Distributions 	 */
194*94d3b452SApple OSS Distributions 	while (cmd->cmd != LC_SEGMENT && cmd->cmd != LC_SEGMENT_64) {
195*94d3b452SApple OSS Distributions 		cmd = (struct load_command *) ((uintptr_t) cmd + cmd->cmdsize);
196*94d3b452SApple OSS Distributions 		nsegs--;
197*94d3b452SApple OSS Distributions 	}
198*94d3b452SApple OSS Distributions 
199*94d3b452SApple OSS Distributions 	segs = calloc(nsegs, sizeof(*segs));
200*94d3b452SApple OSS Distributions 	T_ASSERT_NOTNULL(segs, "calloc disk segment array");
201*94d3b452SApple OSS Distributions 
202*94d3b452SApple OSS Distributions 	for (uint8_t i = 0; i < nsegs; i++) {
203*94d3b452SApple OSS Distributions 		if (cmd->cmd == LC_SEGMENT) {
204*94d3b452SApple OSS Distributions 			struct segment_command *sg = (struct segment_command *) cmd;
205*94d3b452SApple OSS Distributions 			if (sg->vmsize > 0) {
206*94d3b452SApple OSS Distributions 				segs[i] = sg->vmaddr;
207*94d3b452SApple OSS Distributions 			}
208*94d3b452SApple OSS Distributions 		} else if (cmd->cmd == LC_SEGMENT_64) {
209*94d3b452SApple OSS Distributions 			struct segment_command_64 *sg = (struct segment_command_64 *) cmd;
210*94d3b452SApple OSS Distributions 			if (sg->vmsize > 0) {
211*94d3b452SApple OSS Distributions 				segs[i] = sg->vmaddr;
212*94d3b452SApple OSS Distributions 			}
213*94d3b452SApple OSS Distributions 		}
214*94d3b452SApple OSS Distributions 		cmd = (struct load_command *) ((uintptr_t) cmd + cmd->cmdsize);
215*94d3b452SApple OSS Distributions 	}
216*94d3b452SApple OSS Distributions 
217*94d3b452SApple OSS Distributions 	*segs_out = segs;
218*94d3b452SApple OSS Distributions 	*nsegs_out = nsegs;
219*94d3b452SApple OSS Distributions 
220*94d3b452SApple OSS Distributions 	err = munmap(data, (size_t)sb.st_size);
221*94d3b452SApple OSS Distributions 
222*94d3b452SApple OSS Distributions 	err = close(fd);
223*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "close kernel fd");
224*94d3b452SApple OSS Distributions }
225*94d3b452SApple OSS Distributions 
226*94d3b452SApple OSS Distributions static bool
is_fileset_kc(void)227*94d3b452SApple OSS Distributions is_fileset_kc(void)
228*94d3b452SApple OSS Distributions {
229*94d3b452SApple OSS Distributions 	char uuid[1024];
230*94d3b452SApple OSS Distributions 	int err;
231*94d3b452SApple OSS Distributions 	size_t size = sizeof(uuid);
232*94d3b452SApple OSS Distributions 	err = sysctlbyname("kern.filesetuuid", uuid, &size, NULL, 0);
233*94d3b452SApple OSS Distributions 	return err == 0;
234*94d3b452SApple OSS Distributions }
235*94d3b452SApple OSS Distributions 
236*94d3b452SApple OSS Distributions #define KAS_INFO_KERNEL_SEGMENT_LOCATION_SELECTOR 1
237*94d3b452SApple OSS Distributions 
238*94d3b452SApple OSS Distributions T_DECL(kernel_segment_location,
239*94d3b452SApple OSS Distributions     "ensures that KAS_INFO_KERNEL_SEGMENT_LOCATION returns correct segment locations")
240*94d3b452SApple OSS Distributions {
241*94d3b452SApple OSS Distributions 	int err;
242*94d3b452SApple OSS Distributions 
243*94d3b452SApple OSS Distributions 	if (!slide_enabled()) {
244*94d3b452SApple OSS Distributions 		T_SKIP("KASLR is not enabled");
245*94d3b452SApple OSS Distributions 		__builtin_unreachable();
246*94d3b452SApple OSS Distributions 	}
247*94d3b452SApple OSS Distributions 
248*94d3b452SApple OSS Distributions 	uint64_t *disk_segs;
249*94d3b452SApple OSS Distributions 	size_t disk_nsegs;
250*94d3b452SApple OSS Distributions 	disk_kernel_segments(&disk_segs, &disk_nsegs);
251*94d3b452SApple OSS Distributions 
252*94d3b452SApple OSS Distributions 	size_t size = 0;
253*94d3b452SApple OSS Distributions 
254*94d3b452SApple OSS Distributions 	err = kas_info(KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR, NULL, &size);
255*94d3b452SApple OSS Distributions 	if (errno == ENOTSUP) {
256*94d3b452SApple OSS Distributions 		T_SKIP("KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR not supported");
257*94d3b452SApple OSS Distributions 	}
258*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "kas_info KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR for size");
259*94d3b452SApple OSS Distributions 
260*94d3b452SApple OSS Distributions 	uint64_t mem_nsegs = size / sizeof(uint64_t);
261*94d3b452SApple OSS Distributions 	uint64_t *mem_segs = calloc(mem_nsegs, sizeof(*disk_segs));
262*94d3b452SApple OSS Distributions 
263*94d3b452SApple OSS Distributions 	err = kas_info(KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR, mem_segs, &size);
264*94d3b452SApple OSS Distributions 	if (errno == ENOTSUP) {
265*94d3b452SApple OSS Distributions 		T_SKIP("KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR not supported");
266*94d3b452SApple OSS Distributions 	}
267*94d3b452SApple OSS Distributions 
268*94d3b452SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "kas_info KAS_INFO_KERNEL_SEGMENT_VMADDR_SELECTOR for data");
269*94d3b452SApple OSS Distributions 
270*94d3b452SApple OSS Distributions 	T_LOG("Kernel has %zu segments on disk, %zu in memory:", disk_nsegs, mem_nsegs);
271*94d3b452SApple OSS Distributions 	for (size_t i = 0; i < disk_nsegs; i++) {
272*94d3b452SApple OSS Distributions 		T_LOG("%zu %llx %llx", i, disk_segs[i], mem_segs[i]);
273*94d3b452SApple OSS Distributions 	}
274*94d3b452SApple OSS Distributions 
275*94d3b452SApple OSS Distributions 	/*
276*94d3b452SApple OSS Distributions 	 * If the kernel is not a fileset, verify that all
277*94d3b452SApple OSS Distributions 	 * the segments in memory are the segment on disk
278*94d3b452SApple OSS Distributions 	 * + the kaslr slide
279*94d3b452SApple OSS Distributions 	 */
280*94d3b452SApple OSS Distributions 	if (!is_fileset_kc()) {
281*94d3b452SApple OSS Distributions 		T_LOG("Kernelcache is not a fileset kernelcache");
282*94d3b452SApple OSS Distributions 
283*94d3b452SApple OSS Distributions 		uint64_t slide = kernel_slide(KAS_INFO_KERNEL_TEXT_SLIDE_SELECTOR);
284*94d3b452SApple OSS Distributions 		for (size_t i = 0; i < disk_nsegs; i++) {
285*94d3b452SApple OSS Distributions 			if (disk_segs[i] == 0 || mem_segs[i] == 0) {
286*94d3b452SApple OSS Distributions 				continue;
287*94d3b452SApple OSS Distributions 			}
288*94d3b452SApple OSS Distributions 			T_ASSERT_EQ(disk_segs[i] + slide, mem_segs[i], "segment %zu is slid", i);
289*94d3b452SApple OSS Distributions 		}
290*94d3b452SApple OSS Distributions 	}
291*94d3b452SApple OSS Distributions 
292*94d3b452SApple OSS Distributions 	free(disk_segs);
293*94d3b452SApple OSS Distributions 	free(mem_segs);
294*94d3b452SApple OSS Distributions }
295