1*33de042dSApple OSS Distributions // Copyright (c) 2020 Apple, Inc. All rights reserved.
2*33de042dSApple OSS Distributions
3*33de042dSApple OSS Distributions #include <darwintest.h>
4*33de042dSApple OSS Distributions #include <dirent.h>
5*33de042dSApple OSS Distributions #include <fcntl.h>
6*33de042dSApple OSS Distributions #include <libkern/OSByteOrder.h>
7*33de042dSApple OSS Distributions #include <mach-o/loader.h>
8*33de042dSApple OSS Distributions #include <stdbool.h>
9*33de042dSApple OSS Distributions #include <sys/mman.h>
10*33de042dSApple OSS Distributions #include <sys/stat.h>
11*33de042dSApple OSS Distributions #include <sys/sysctl.h>
12*33de042dSApple OSS Distributions #include <TargetConditionals.h>
13*33de042dSApple OSS Distributions #include <unistd.h>
14*33de042dSApple OSS Distributions #include <uuid/uuid.h>
15*33de042dSApple OSS Distributions
16*33de042dSApple OSS Distributions static bool
get_macho_uuid(const char * cwd,const char * path,uuid_t uuid)17*33de042dSApple OSS Distributions get_macho_uuid(const char *cwd, const char *path, uuid_t uuid)
18*33de042dSApple OSS Distributions {
19*33de042dSApple OSS Distributions bool found = false;
20*33de042dSApple OSS Distributions void *mapped = MAP_FAILED;
21*33de042dSApple OSS Distributions size_t mapped_len = 0;
22*33de042dSApple OSS Distributions
23*33de042dSApple OSS Distributions T_SETUPBEGIN;
24*33de042dSApple OSS Distributions
25*33de042dSApple OSS Distributions // Skip irregular files (directories, devices, etc.).
26*33de042dSApple OSS Distributions struct stat stbuf = {};
27*33de042dSApple OSS Distributions int ret = stat(path, &stbuf);
28*33de042dSApple OSS Distributions if (ret < 0 && errno == ENOENT) {
29*33de042dSApple OSS Distributions goto out;
30*33de042dSApple OSS Distributions }
31*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "should stat %s%s", cwd, path);
32*33de042dSApple OSS Distributions if ((stbuf.st_mode & S_IFREG) == 0) {
33*33de042dSApple OSS Distributions goto out;
34*33de042dSApple OSS Distributions }
35*33de042dSApple OSS Distributions if (stbuf.st_size < (off_t)sizeof(struct mach_header)) {
36*33de042dSApple OSS Distributions goto out;
37*33de042dSApple OSS Distributions }
38*33de042dSApple OSS Distributions
39*33de042dSApple OSS Distributions int fd = open(path, O_RDONLY);
40*33de042dSApple OSS Distributions if (fd < 0 && (errno == EPERM || errno == EACCES || errno == ENOENT)) {
41*33de042dSApple OSS Distributions goto out;
42*33de042dSApple OSS Distributions }
43*33de042dSApple OSS Distributions T_QUIET;
44*33de042dSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(fd, "should open file at %s%s", cwd, path);
45*33de042dSApple OSS Distributions
46*33de042dSApple OSS Distributions mapped = mmap(NULL, (size_t)stbuf.st_size, PROT_READ, MAP_PRIVATE,
47*33de042dSApple OSS Distributions fd, 0);
48*33de042dSApple OSS Distributions T_QUIET; T_WITH_ERRNO;
49*33de042dSApple OSS Distributions T_ASSERT_NE(mapped, MAP_FAILED, "should map Mach-O binary at %s%s",
50*33de042dSApple OSS Distributions cwd, path);
51*33de042dSApple OSS Distributions (void)close(fd);
52*33de042dSApple OSS Distributions
53*33de042dSApple OSS Distributions // Mach-O parsing boilerplate.
54*33de042dSApple OSS Distributions uint32_t magic = *(uint32_t *)mapped;
55*33de042dSApple OSS Distributions bool should_swap = false;
56*33de042dSApple OSS Distributions bool b32 = false;
57*33de042dSApple OSS Distributions // XXX This does not handle fat binaries.
58*33de042dSApple OSS Distributions switch (magic) {
59*33de042dSApple OSS Distributions case MH_CIGAM:
60*33de042dSApple OSS Distributions should_swap = true;
61*33de042dSApple OSS Distributions OS_FALLTHROUGH;
62*33de042dSApple OSS Distributions case MH_MAGIC:
63*33de042dSApple OSS Distributions b32 = true;
64*33de042dSApple OSS Distributions break;
65*33de042dSApple OSS Distributions case MH_CIGAM_64:
66*33de042dSApple OSS Distributions should_swap = true;
67*33de042dSApple OSS Distributions break;
68*33de042dSApple OSS Distributions case MH_MAGIC_64:
69*33de042dSApple OSS Distributions break;
70*33de042dSApple OSS Distributions default:
71*33de042dSApple OSS Distributions goto out;
72*33de042dSApple OSS Distributions }
73*33de042dSApple OSS Distributions const struct load_command *lcmd = NULL;
74*33de042dSApple OSS Distributions unsigned int ncmds = 0;
75*33de042dSApple OSS Distributions if (b32) {
76*33de042dSApple OSS Distributions const struct mach_header *hdr = mapped;
77*33de042dSApple OSS Distributions ncmds = hdr->ncmds;
78*33de042dSApple OSS Distributions lcmd = (const void *)((const char *)mapped + sizeof(*hdr));
79*33de042dSApple OSS Distributions } else {
80*33de042dSApple OSS Distributions const struct mach_header_64 *hdr = mapped;
81*33de042dSApple OSS Distributions ncmds = hdr->ncmds;
82*33de042dSApple OSS Distributions lcmd = (const void *)((const char *)mapped + sizeof(*hdr));
83*33de042dSApple OSS Distributions }
84*33de042dSApple OSS Distributions ncmds = should_swap ? OSSwapInt32(ncmds) : ncmds;
85*33de042dSApple OSS Distributions
86*33de042dSApple OSS Distributions // Scan through load commands to find LC_UUID.
87*33de042dSApple OSS Distributions for (unsigned int i = 0; i < ncmds; i++) {
88*33de042dSApple OSS Distributions if ((should_swap ? OSSwapInt32(lcmd->cmd) : lcmd->cmd) == LC_UUID) {
89*33de042dSApple OSS Distributions const struct uuid_command *uuid_cmd = (const void *)lcmd;
90*33de042dSApple OSS Distributions uuid_copy(uuid, uuid_cmd->uuid);
91*33de042dSApple OSS Distributions found = true;
92*33de042dSApple OSS Distributions break;
93*33de042dSApple OSS Distributions }
94*33de042dSApple OSS Distributions
95*33de042dSApple OSS Distributions uint32_t cmdsize = should_swap ? OSSwapInt32(lcmd->cmdsize) :
96*33de042dSApple OSS Distributions lcmd->cmdsize;
97*33de042dSApple OSS Distributions lcmd = (const void *)((const char *)lcmd + cmdsize);
98*33de042dSApple OSS Distributions }
99*33de042dSApple OSS Distributions
100*33de042dSApple OSS Distributions if (!found) {
101*33de042dSApple OSS Distributions T_LOG("could not find LC_UUID in Mach-O at %s%s", cwd, path);
102*33de042dSApple OSS Distributions }
103*33de042dSApple OSS Distributions
104*33de042dSApple OSS Distributions out:
105*33de042dSApple OSS Distributions T_SETUPEND;
106*33de042dSApple OSS Distributions
107*33de042dSApple OSS Distributions if (mapped != MAP_FAILED) {
108*33de042dSApple OSS Distributions munmap(mapped, mapped_len);
109*33de042dSApple OSS Distributions }
110*33de042dSApple OSS Distributions return found;
111*33de042dSApple OSS Distributions }
112*33de042dSApple OSS Distributions
113*33de042dSApple OSS Distributions T_DECL(correct_kernel_booted,
114*33de042dSApple OSS Distributions "Make sure the kernel on disk matches the running kernel, by UUID.",
115*33de042dSApple OSS Distributions T_META_RUN_CONCURRENTLY(true),
116*33de042dSApple OSS Distributions T_META_CHECK_LEAKS(false), T_META_TAG_VM_NOT_PREFERRED)
117*33de042dSApple OSS Distributions {
118*33de042dSApple OSS Distributions T_SETUPBEGIN;
119*33de042dSApple OSS Distributions
120*33de042dSApple OSS Distributions uuid_t kern_uuid;
121*33de042dSApple OSS Distributions uuid_string_t kern_uuid_str;
122*33de042dSApple OSS Distributions size_t kern_uuid_size = sizeof(kern_uuid_str);
123*33de042dSApple OSS Distributions int ret = sysctlbyname("kern.uuid", &kern_uuid_str, &kern_uuid_size, NULL,
124*33de042dSApple OSS Distributions 0);
125*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(ret, "should get running kernel UUID");
126*33de042dSApple OSS Distributions T_LOG("%s: running kernel", kern_uuid_str);
127*33de042dSApple OSS Distributions
128*33de042dSApple OSS Distributions ret = uuid_parse(kern_uuid_str, kern_uuid);
129*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_EQ(ret, 0, "should parse kernel UUID into bytes");
130*33de042dSApple OSS Distributions
131*33de042dSApple OSS Distributions #if TARGET_OS_OSX
132*33de042dSApple OSS Distributions const char *kernels_path = "/System/Library/Kernels/";
133*33de042dSApple OSS Distributions #else // TARGET_OS_OSX
134*33de042dSApple OSS Distributions const char *kernels_path = "/";
135*33de042dSApple OSS Distributions #endif // !TARGET_OS_OSX
136*33de042dSApple OSS Distributions T_LOG("searching for kernels at %s", kernels_path);
137*33de042dSApple OSS Distributions
138*33de042dSApple OSS Distributions ret = chdir(kernels_path);
139*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "should change directory to %s",
140*33de042dSApple OSS Distributions kernels_path);
141*33de042dSApple OSS Distributions
142*33de042dSApple OSS Distributions DIR *kernels_dir = opendir(kernels_path);
143*33de042dSApple OSS Distributions T_QUIET; T_ASSERT_NOTNULL(kernels_dir, "should open directory at %s",
144*33de042dSApple OSS Distributions kernels_path);
145*33de042dSApple OSS Distributions
146*33de042dSApple OSS Distributions T_SETUPEND;
147*33de042dSApple OSS Distributions
148*33de042dSApple OSS Distributions bool found = false;
149*33de042dSApple OSS Distributions struct dirent *entry = NULL;
150*33de042dSApple OSS Distributions while ((entry = readdir(kernels_dir)) != NULL) {
151*33de042dSApple OSS Distributions uuid_t bin_uuid;
152*33de042dSApple OSS Distributions bool ok = get_macho_uuid(kernels_path, entry->d_name, bin_uuid);
153*33de042dSApple OSS Distributions if (ok) {
154*33de042dSApple OSS Distributions uuid_string_t bin_uuid_str;
155*33de042dSApple OSS Distributions uuid_unparse(bin_uuid, bin_uuid_str);
156*33de042dSApple OSS Distributions T_LOG("%s: from %s%s", bin_uuid_str, kernels_path, entry->d_name);
157*33de042dSApple OSS Distributions if (uuid_compare(bin_uuid, kern_uuid) == 0) {
158*33de042dSApple OSS Distributions found = true;
159*33de042dSApple OSS Distributions T_PASS("UUID from %s%s matches kernel UUID", kernels_path,
160*33de042dSApple OSS Distributions entry->d_name);
161*33de042dSApple OSS Distributions }
162*33de042dSApple OSS Distributions }
163*33de042dSApple OSS Distributions }
164*33de042dSApple OSS Distributions if (!found) {
165*33de042dSApple OSS Distributions T_FAIL("failed to find kernel binary with UUID of the running kernel, "
166*33de042dSApple OSS Distributions "wrong kernel is booted");
167*33de042dSApple OSS Distributions }
168*33de042dSApple OSS Distributions (void)closedir(kernels_dir);
169*33de042dSApple OSS Distributions }
170