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