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