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