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