]> git.saurik.com Git - apple/xnu.git/blob - tests/kernel_uuid_match.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / tests / kernel_uuid_match.c
1 #include <darwintest.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdbool.h>
7 #include <uuid/uuid.h>
8 #include <sys/sysctl.h>
9 #include <TargetConditionals.h>
10 #include <glob.h>
11 #include <sys/stat.h>
12 #include <sys/mman.h>
13 #include <fcntl.h>
14 #include <mach-o/loader.h>
15 #include <mach-o/dyld.h>
16 #include <mach-o/swap.h>
17 #include <libkern/OSByteOrder.h>
18
19 #define MAX_LEN 1024
20
21 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
22 //running on macOS
23 #define KERNEL_SEARCH_DIR "/System/Library/Kernels/*"
24 #else
25 //running on a different OS (e.g. iOS, watchOS, etc.)
26 #define KERNEL_SEARCH_DIR "/*"
27 #endif
28
29 #define SWAP32(v) v = OSSwapInt32(v)
30
31
32 /* opens and maps the file at [path] in memory,
33 * sets the length in [len] and returns a pointer
34 * to the beginning of the memory region or NULL
35 * if unable to open and map the file
36 */
37 static void *open_file(char *path, size_t *len) {
38 int fd;
39 if ((fd = open(path, O_RDONLY)) < 0) {
40 return NULL;
41 }
42 *len = (size_t)lseek(fd, (off_t)0, SEEK_END);
43 void *p = mmap(NULL, *len, PROT_READ, MAP_PRIVATE, fd, 0);
44 close(fd);
45 if (p == MAP_FAILED) {
46 return NULL;
47 }
48 return p;
49 }
50
51 #pragma clang diagnostic push
52 #pragma clang diagnostic ignored "-Wsign-conversion"
53 static void __swap_mach_header(struct mach_header *header) {
54 SWAP32(header->magic);
55 SWAP32(header->cputype);
56 SWAP32(header->cpusubtype);
57 SWAP32(header->filetype);
58 SWAP32(header->ncmds);
59 SWAP32(header->sizeofcmds);
60 SWAP32(header->flags);
61 }
62
63 static void __swap_mach_header_64(struct mach_header_64 *header) {
64 SWAP32(header->magic);
65 SWAP32(header->cputype);
66 SWAP32(header->cpusubtype);
67 SWAP32(header->filetype);
68 SWAP32(header->ncmds);
69 SWAP32(header->sizeofcmds);
70 SWAP32(header->flags);
71 }
72 #pragma clang diagnostic pop
73
74 /* parses the uuid from the file at [path] and sets the uuid in [uuid]
75 * returns true if successfully parses the file, returns false otherwise
76 * (e.g. the file is not a Mach-O binary)
77 */
78 static bool parse_binary_uuid(char *path, uuid_t uuid) {
79 size_t len = 0;
80 bool should_swap = false;
81 unsigned int ncmds = 0;
82 struct load_command *lc = NULL;
83 bool ret = false;
84
85 struct mach_header *h = open_file(path, &len);
86 if (!h) {
87 return false;
88 }
89 if (h->magic == MH_MAGIC || h->magic == MH_CIGAM) {
90 //32-bit header
91 struct mach_header *header = h;
92 if (header->magic == MH_CIGAM) {
93 __swap_mach_header(header);
94 should_swap = true;
95 }
96 ncmds = header->ncmds;
97 //the first load command is after the header
98 lc = (struct load_command *)(header + 1);
99 } else if (h->magic == MH_MAGIC_64 || h->magic == MH_CIGAM_64) {
100 //64-bit header
101 struct mach_header_64 *header = (struct mach_header_64 *)h;
102 if (header->magic == MH_CIGAM_64) {
103 __swap_mach_header_64(header);
104 should_swap = true;
105 }
106 ncmds = header->ncmds;
107 lc = (struct load_command *)(header + 1);
108 } else {
109 //this is not a Mach-O binary, or it is a FAT binary
110 munmap(h, len);
111 return false;
112 }
113 for (unsigned int i = 0; i < ncmds; i++) {
114 uint32_t cmd = lc->cmd;
115 uint32_t cmdsize = lc->cmdsize;
116 if (should_swap) {
117 SWAP32(cmd);
118 SWAP32(cmdsize);
119 }
120 if (cmd == LC_UUID) {
121 struct uuid_command *uuid_cmd =
122 (struct uuid_command *)lc;
123 uuid_copy(uuid, uuid_cmd->uuid);
124 uuid_string_t tuuid_str;
125 uuid_unparse(uuid, tuuid_str);
126 T_LOG("Trying test UUID %s", tuuid_str);
127 ret = true;
128 break;
129 }
130 lc = (struct load_command *)((uintptr_t)lc + cmdsize);
131 }
132 munmap(h, len);
133 return ret;
134 }
135
136 /* uses the sysctl command line tool to get the uuid
137 * of the currently running kernel
138 */
139 static void get_system_kernel_uuid(uuid_t kuuid) {
140 char kuuid_line[MAX_LEN];
141 memset(kuuid_line, 0, sizeof(kuuid_line));
142 size_t len = sizeof(kuuid_line);
143 int ret = sysctlbyname("kern.uuid", kuuid_line, &len, NULL, 0);
144 T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.uuid");
145
146 T_ASSERT_TRUE(uuid_parse(kuuid_line, kuuid) == 0,
147 "Parse running kernel uuid");
148 }
149
150 /* compares [kuuid] to the uuid in each of the kernel binaries on OS's
151 * other than macOS (there can be multiple kernel binaries if the mastering
152 * process doesn't remove all of the irrelevant binaries)
153 */
154 static void find_and_compare_test_uuids(char *search_path, uuid_t kuuid) {
155 glob_t g;
156 int ret = glob(search_path, 0, NULL, &g);
157 T_WITH_ERRNO; T_ASSERT_EQ(ret, 0, "glob %s", search_path);
158
159 bool pass = false;
160 for (int i = 0; i < g.gl_matchc; i++) {
161 char *path = g.gl_pathv[i];
162
163 //check that [path] is the path for a file (not a directory, device, etc.)
164 struct stat s;
165 int ret = stat(path, &s);
166 T_ASSERT_POSIX_SUCCESS(ret, "stat %s", path);
167 if ((s.st_mode & S_IFREG) == 0) {
168 continue;
169 }
170
171 T_LOG("Reading file at path: %s", path);
172 uuid_t tuuid;
173 if (parse_binary_uuid(path, tuuid) &&
174 uuid_compare(kuuid, tuuid) == 0) {
175 pass = true;
176 break;
177 }
178 }
179 globfree(&g);
180 T_EXPECT_TRUE(pass, "The sources match");
181 }
182
183 T_DECL(uuid_match, "Compare the running kernel UUID to kernel binaries.")
184 {
185 uuid_t kuuid;
186 uuid_clear(kuuid);
187 get_system_kernel_uuid(kuuid);
188 uuid_string_t kuuid_str;
189 uuid_unparse(kuuid, kuuid_str);
190 T_LOG("Got running kernel UUID %s", kuuid_str);
191 find_and_compare_test_uuids(KERNEL_SEARCH_DIR, kuuid);
192 }