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