]> git.saurik.com Git - apple/xnu.git/blame - tests/kernel_uuid_match.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / tests / kernel_uuid_match.c
CommitLineData
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
19T_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 */
39static 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"
55static 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
65static 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 */
80static 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 */
141static 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 */
156static 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
185T_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}