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