1 // Copyright (c) 2020 Apple, Inc. All rights reserved.
3 #include <darwintest.h>
6 #include <libkern/OSByteOrder.h>
7 #include <mach-o/loader.h>
11 #include <sys/sysctl.h>
12 #include <TargetConditionals.h>
14 #include <uuid/uuid.h>
17 get_macho_uuid(const char *cwd
, const char *path
, uuid_t uuid
)
20 void *mapped
= MAP_FAILED
;
21 size_t mapped_len
= 0;
25 // Skip irregular files (directories, devices, etc.).
26 struct stat stbuf
= {};
27 int ret
= stat(path
, &stbuf
);
28 if (ret
< 0 && errno
== ENOENT
) {
31 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "should stat %s%s", cwd
, path
);
32 if ((stbuf
.st_mode
& S_IFREG
) == 0) {
35 if (stbuf
.st_size
< (off_t
)sizeof(struct mach_header
)) {
39 int fd
= open(path
, O_RDONLY
);
40 if (fd
< 0 && (errno
== EPERM
|| errno
== EACCES
|| errno
== ENOENT
)) {
44 T_ASSERT_POSIX_SUCCESS(fd
, "should open file at %s%s", cwd
, path
);
46 mapped
= mmap(NULL
, (size_t)stbuf
.st_size
, PROT_READ
, MAP_PRIVATE
,
48 T_QUIET
; T_WITH_ERRNO
;
49 T_ASSERT_NE(mapped
, MAP_FAILED
, "should map Mach-O binary at %s%s",
53 // Mach-O parsing boilerplate.
54 uint32_t magic
= *(uint32_t *)mapped
;
55 bool should_swap
= false;
57 // XXX This does not handle fat binaries.
73 const struct load_command
*lcmd
= NULL
;
74 unsigned int ncmds
= 0;
76 const struct mach_header
*hdr
= mapped
;
78 lcmd
= (const void *)((const char *)mapped
+ sizeof(*hdr
));
80 const struct mach_header_64
*hdr
= mapped
;
82 lcmd
= (const void *)((const char *)mapped
+ sizeof(*hdr
));
84 ncmds
= should_swap
? OSSwapInt32(ncmds
) : ncmds
;
86 // Scan through load commands to find LC_UUID.
87 for (unsigned int i
= 0; i
< ncmds
; i
++) {
88 if ((should_swap
? OSSwapInt32(lcmd
->cmd
) : lcmd
->cmd
) == LC_UUID
) {
89 const struct uuid_command
*uuid_cmd
= (const void *)lcmd
;
90 uuid_copy(uuid
, uuid_cmd
->uuid
);
95 uint32_t cmdsize
= should_swap
? OSSwapInt32(lcmd
->cmdsize
) :
97 lcmd
= (const void *)((const char *)lcmd
+ cmdsize
);
101 T_LOG("could not find LC_UUID in Mach-O at %s%s", cwd
, path
);
107 if (mapped
!= MAP_FAILED
) {
108 munmap(mapped
, mapped_len
);
113 T_DECL(correct_kernel_booted
,
114 "Make sure the kernel on disk matches the running kernel, by UUID.",
115 T_META_RUN_CONCURRENTLY(true))
120 uuid_string_t kern_uuid_str
;
121 size_t kern_uuid_size
= sizeof(kern_uuid_str
);
122 int ret
= sysctlbyname("kern.uuid", &kern_uuid_str
, &kern_uuid_size
, NULL
,
124 T_QUIET
; T_ASSERT_POSIX_ZERO(ret
, "should get running kernel UUID");
125 T_LOG("%s: running kernel", kern_uuid_str
);
127 ret
= uuid_parse(kern_uuid_str
, kern_uuid
);
128 T_QUIET
; T_ASSERT_EQ(ret
, 0, "should parse kernel UUID into bytes");
131 const char *kernels_path
= "/System/Library/Kernels/";
132 #else // TARGET_OS_OSX
133 const char *kernels_path
= "/";
134 #endif // !TARGET_OS_OSX
135 T_LOG("searching for kernels at %s", kernels_path
);
137 ret
= chdir(kernels_path
);
138 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "should change directory to %s",
141 DIR *kernels_dir
= opendir(kernels_path
);
142 T_QUIET
; T_ASSERT_NOTNULL(kernels_dir
, "should open directory at %s",
148 struct dirent
*entry
= NULL
;
149 while ((entry
= readdir(kernels_dir
)) != NULL
) {
151 bool ok
= get_macho_uuid(kernels_path
, entry
->d_name
, bin_uuid
);
153 uuid_string_t bin_uuid_str
;
154 uuid_unparse(bin_uuid
, bin_uuid_str
);
155 T_LOG("%s: from %s%s", bin_uuid_str
, kernels_path
, entry
->d_name
);
156 if (uuid_compare(bin_uuid
, kern_uuid
) == 0) {
158 T_PASS("UUID from %s%s matches kernel UUID", kernels_path
,
164 T_FAIL("failed to find kernel binary with UUID of the running kernel, "
165 "wrong kernel is booted");