]> git.saurik.com Git - apple/xnu.git/blame - tests/system_version_compat.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / tests / system_version_compat.c
CommitLineData
f427ee49
A
1#include <darwintest.h>
2#include <darwintest_utils.h>
3#include <dispatch/dispatch.h>
4#include <fcntl.h>
5#include <stdio.h>
6#include <TargetConditionals.h>
7#include <unistd.h>
8#include <xpc/private.h>
9
10#define SYSTEM_VERSION_COMPAT_PLIST_PATH "/System/Library/CoreServices/SystemVersionCompat.plist"
11#define IOS_SYSTEM_VERSION_PLIST_PATH "/System/Library/CoreServices/iOSSystemVersion.plist"
12
13#define SYSTEM_VERSION_PLIST_FILENAME "SystemVersion.plist"
14#define SYSTEM_VERSION_PLIST_PATH ("/System/Library/CoreServices/" SYSTEM_VERSION_PLIST_FILENAME)
15
16#define PRODUCT_VERSION_KEY "ProductVersion"
17#define IOS_SUPPORT_VERSION_KEY "iOSSupportVersion"
18
19#define PRODUCT_VERSION_SYSCTL "kern.osproductversion"
20#define PRODUCT_VERSION_COMPAT_SYSCTL "kern.osproductversioncompat"
21
22T_GLOBAL_META(T_META_CHECK_LEAKS(false));
23
24#if TARGET_OS_OSX
25static void
26check_system_version_compat_plist_exists(void)
27{
28 struct stat buf;
29
30 int ret = stat(SYSTEM_VERSION_COMPAT_PLIST_PATH, &buf);
31 int error = errno;
32 if (ret != 0) {
33 if (error == ENOENT) {
34 T_SKIP("no SystemVersionCompat.plist on this system in %s, skipping test...",
35 SYSTEM_VERSION_COMPAT_PLIST_PATH);
36 } else {
37 T_ASSERT_FAIL("failed to find SystemVersionCompat.plist at " IOS_SYSTEM_VERSION_PLIST_PATH "with error: %s",
38 strerror(error));
39 }
40 }
41}
42
43static void
44check_ios_version_plist_exists(void)
45{
46 struct stat buf;
47
48 int ret = stat(IOS_SYSTEM_VERSION_PLIST_PATH, &buf);
49 int error = errno;
50 if (ret != 0) {
51 if (errno == ENOENT) {
52 T_SKIP("no iOSSystemVersion.plist on this system in %s, skipping test...",
53 IOS_SYSTEM_VERSION_PLIST_PATH);
54 } else {
55 T_ASSERT_FAIL("failed to find iOSSystemVersion.plist at " IOS_SYSTEM_VERSION_PLIST_PATH "with error: %s",
56 strerror(error));
57 }
58 }
59}
60
61static void
62read_plist_version_info(char **version_plist_vers, char **compat_version_plist_vers, bool expect_shim)
63{
64 char opened_path[MAXPATHLEN] = { '\0' };
65
66 int version_plist_fd = open(SYSTEM_VERSION_PLIST_PATH, O_RDONLY);
67 T_QUIET; T_WITH_ERRNO; T_ASSERT_GT(version_plist_fd, 0, "opened %s", SYSTEM_VERSION_PLIST_PATH);
68
69 // Resolve the full path of the file we've opened, verify it was either shimmed or not (as expected)
70 int ret = fcntl(version_plist_fd, F_GETPATH, opened_path);
71 T_QUIET; T_WITH_ERRNO; T_EXPECT_NE(ret, -1, "F_GETPATH on opened SystemVersion.plist");
72 if (ret != -1) {
73 size_t opened_path_strlen = strlen(opened_path);
74 if (expect_shim) {
75 T_QUIET; T_EXPECT_GE(opened_path_strlen, strlen(SYSTEM_VERSION_COMPAT_PLIST_PATH), "opened path string length");
76 T_EXPECT_EQ_STR(SYSTEM_VERSION_COMPAT_PLIST_PATH, (const char *)&opened_path[(opened_path_strlen - strlen(SYSTEM_VERSION_COMPAT_PLIST_PATH))],
77 "opened file path shimmed (Mac OS)");
78 } else {
79 T_QUIET; T_EXPECT_GE(opened_path_strlen, strlen(SYSTEM_VERSION_PLIST_PATH), "opened path string length");
80 T_EXPECT_EQ_STR(SYSTEM_VERSION_PLIST_PATH, (const char *)&opened_path[(opened_path_strlen - strlen(SYSTEM_VERSION_PLIST_PATH))],
81 "opened file path not shimmed");
82 }
83 }
84
85 // Read and parse the plists
86 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
87 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
88 xpc_create_from_plist_descriptor(version_plist_fd, queue, ^(xpc_object_t object) {
89 if (object == NULL) {
90 T_ASSERT_FAIL("Failed to parse dictionary from %s", SYSTEM_VERSION_PLIST_PATH);
91 }
92 if (xpc_get_type(object) != XPC_TYPE_DICTIONARY) {
93 T_ASSERT_FAIL("%s does not contain dictionary plist", SYSTEM_VERSION_PLIST_PATH);
94 }
95
96 const char *plist_version = xpc_dictionary_get_string(object, PRODUCT_VERSION_KEY);
97 if (plist_version) {
98 T_LOG("Found %s for %s from %s", plist_version, PRODUCT_VERSION_KEY, SYSTEM_VERSION_PLIST_PATH);
99 *version_plist_vers = strdup(plist_version);
100 }
101 dispatch_semaphore_signal(sema);
102 });
103 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
104
105 close(version_plist_fd);
106 version_plist_fd = -1;
107
108 int compat_version_plist_fd = open(SYSTEM_VERSION_COMPAT_PLIST_PATH, O_RDONLY);
109 T_QUIET; T_WITH_ERRNO; T_ASSERT_GT(compat_version_plist_fd, 0, "opened %s", SYSTEM_VERSION_COMPAT_PLIST_PATH);
110
111 xpc_create_from_plist_descriptor(compat_version_plist_fd, queue, ^(xpc_object_t object) {
112 if (object == NULL) {
113 T_ASSERT_FAIL("Failed to parse dictionary from %s", SYSTEM_VERSION_COMPAT_PLIST_PATH);
114 }
115 if (xpc_get_type(object) != XPC_TYPE_DICTIONARY) {
116 T_ASSERT_FAIL("%s does not contain dictionary plist", SYSTEM_VERSION_COMPAT_PLIST_PATH);
117 }
118
119 const char *plist_version = xpc_dictionary_get_string(object, PRODUCT_VERSION_KEY);
120 if (plist_version) {
121 T_LOG("Found %s for %s from %s", plist_version, PRODUCT_VERSION_KEY, SYSTEM_VERSION_COMPAT_PLIST_PATH);
122 *compat_version_plist_vers = strdup(plist_version);
123 }
124 dispatch_semaphore_signal(sema);
125 });
126 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
127
128 close(compat_version_plist_fd);
129 compat_version_plist_fd = -1;
130
131 return;
132}
133
134static void
135read_sysctl_version_info(char **vers, char **compat_vers)
136{
137 char version[16] = { '\0' }, compat_version[16] = { '\0' };
138 size_t version_len = sizeof(version), compat_version_len = sizeof(compat_version);
139
140 T_QUIET; T_ASSERT_POSIX_ZERO(sysctlbyname(PRODUCT_VERSION_SYSCTL, version, &version_len, NULL, 0), "read %s", PRODUCT_VERSION_SYSCTL);
141 T_LOG("Foundd %s from %s", version, PRODUCT_VERSION_SYSCTL);
142
143 T_QUIET; T_ASSERT_POSIX_ZERO(sysctlbyname(PRODUCT_VERSION_COMPAT_SYSCTL, compat_version, &compat_version_len, NULL, 0),
144 "read %s", PRODUCT_VERSION_COMPAT_SYSCTL);
145 T_LOG("Found %s from %s", compat_version, PRODUCT_VERSION_COMPAT_SYSCTL);
146
147 *vers = strdup(version);
148 *compat_vers = strdup(compat_version);
149
150 return;
151}
152#endif // TARGET_OS_OSX
153
154T_DECL(test_system_version_compat_disabled,
155 "Tests reading system product information without system version compat enabled")
156{
157#if TARGET_OS_OSX
158 check_system_version_compat_plist_exists();
159 char *plist_vers = NULL, *plist_compat_vers = NULL;
160 char *sysctl_vers = NULL, *sysctl_compat_vers = NULL;
161
162 // Read plist version data
163 read_plist_version_info(&plist_vers, &plist_compat_vers, false);
164
165 // Read sysctl version data
166 read_sysctl_version_info(&sysctl_vers, &sysctl_compat_vers);
167
168 // Verify the normal data matches
169 T_EXPECT_EQ_STR(plist_vers, sysctl_vers, "%s %s matches %s value", SYSTEM_VERSION_PLIST_PATH,
170 PRODUCT_VERSION_KEY, PRODUCT_VERSION_SYSCTL);
171
172 // Verify that the compatibility data matches
173 T_EXPECT_EQ_STR(plist_compat_vers, sysctl_compat_vers, "%s %s matches %s value", SYSTEM_VERSION_COMPAT_PLIST_PATH,
174 PRODUCT_VERSION_KEY, PRODUCT_VERSION_COMPAT_SYSCTL);
175
176
177 free(plist_vers);
178 free(plist_compat_vers);
179 free(sysctl_vers);
180 free(sysctl_compat_vers);
181
182 T_PASS("verified version information without system version compat");
183#else // TARGET_OS_OSX
184 T_SKIP("system version compat only supported on macOS");
185#endif // TARGET_OS_OSX
186}
187
188T_DECL(test_system_version_compat_enabled,
189 "Tests reading system product information with system version compat enabled",
190 T_META_ENVVAR("SYSTEM_VERSION_COMPAT=1"))
191{
192#if TARGET_OS_OSX
193 check_system_version_compat_plist_exists();
194 char *plist_vers = NULL, *plist_compat_vers = NULL;
195 char *sysctl_vers = NULL, *sysctl_compat_vers = NULL;
196
197 // Read plist version data
198 read_plist_version_info(&plist_vers, &plist_compat_vers, true);
199
200 // Read sysctl version data
201 read_sysctl_version_info(&sysctl_vers, &sysctl_compat_vers);
202
203 // The version information should match from all sources with the shim enabled
204
205 // Verify the normal data matches
206 T_EXPECT_EQ_STR(plist_vers, sysctl_vers, "%s %s matches %s value", SYSTEM_VERSION_PLIST_PATH,
207 PRODUCT_VERSION_KEY, PRODUCT_VERSION_SYSCTL);
208
209 // Verify that the compatibility data matches
210 T_EXPECT_EQ_STR(plist_compat_vers, sysctl_compat_vers, "%s %s matches %s value", SYSTEM_VERSION_COMPAT_PLIST_PATH,
211 PRODUCT_VERSION_KEY, PRODUCT_VERSION_COMPAT_SYSCTL);
212
213 // Verify the normal data matches the compatibility data
214 T_EXPECT_EQ_STR(plist_vers, plist_compat_vers, "%s matches in both %s and %s", PRODUCT_VERSION_KEY,
215 SYSTEM_VERSION_PLIST_PATH, SYSTEM_VERSION_COMPAT_PLIST_PATH);
216
217 free(plist_vers);
218 free(plist_compat_vers);
219 free(sysctl_vers);
220 free(sysctl_compat_vers);
221
222 T_PASS("verified version information with Mac OS X shim enabled");
223#else // TARGET_OS_OSX
224 T_SKIP("system version compat only supported on macOS");
225#endif // TARGET_OS_OSX
226}
227
228T_DECL(test_system_version_compat_enabled_ios,
229 "Tests reading system product information with the iOS system version compat shim enabled",
230 T_META_ENVVAR("SYSTEM_VERSION_COMPAT=2"))
231{
232#if TARGET_OS_OSX
233 char opened_path[MAXPATHLEN] = { '\0' };
234
235 check_ios_version_plist_exists();
236
237 // Read out the ProductVersion from SystemVersion.plist and ensure that it contains the same value as the
238 // iOSSupportVersion key
239
240 __block char *read_plist_vers = NULL, *read_ios_support_version = NULL;
241
242 int version_plist_fd = open(SYSTEM_VERSION_PLIST_PATH, O_RDONLY);
243 T_QUIET; T_WITH_ERRNO; T_ASSERT_GT(version_plist_fd, 0, "opened %s", SYSTEM_VERSION_PLIST_PATH);
244
245 // Resolve the full path of the file we've opened, verify it was shimmed as expected
246 int ret = fcntl(version_plist_fd, F_GETPATH, opened_path);
247 T_QUIET; T_WITH_ERRNO; T_EXPECT_NE(ret, -1, "F_GETPATH on opened SystemVersion.plist");
248 if (ret != -1) {
249 size_t opened_path_strlen = strlen(opened_path);
250 T_QUIET; T_EXPECT_GE(opened_path_strlen, strlen(IOS_SYSTEM_VERSION_PLIST_PATH), "opened path string length");
251 T_EXPECT_EQ_STR(IOS_SYSTEM_VERSION_PLIST_PATH, (const char *)&opened_path[(opened_path_strlen - strlen(IOS_SYSTEM_VERSION_PLIST_PATH))],
252 "opened file path shimmed (iOS)");
253 }
254
255 // Read and parse the attributes from the SystemVersion plist
256 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
257 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
258 xpc_create_from_plist_descriptor(version_plist_fd, queue, ^(xpc_object_t object) {
259 if (object == NULL) {
260 T_ASSERT_FAIL("Failed to parse dictionary from %s", SYSTEM_VERSION_PLIST_PATH);
261 }
262 if (xpc_get_type(object) != XPC_TYPE_DICTIONARY) {
263 T_ASSERT_FAIL("%s does not contain dictionary plist", SYSTEM_VERSION_PLIST_PATH);
264 }
265
266 const char *plist_version = xpc_dictionary_get_string(object, PRODUCT_VERSION_KEY);
267 if (plist_version) {
268 T_LOG("Found %s for %s from %s", plist_version, PRODUCT_VERSION_KEY, SYSTEM_VERSION_PLIST_PATH);
269 read_plist_vers = strdup(plist_version);
270 }
271
272 const char *ios_support_version = xpc_dictionary_get_string(object, IOS_SUPPORT_VERSION_KEY);
273 if (ios_support_version) {
274 T_LOG("Found %s for %s from %s", ios_support_version, IOS_SUPPORT_VERSION_KEY, SYSTEM_VERSION_PLIST_PATH);
275 read_ios_support_version = strdup(ios_support_version);
276 }
277
278 dispatch_semaphore_signal(sema);
279 });
280 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
281
282 close(version_plist_fd);
283 version_plist_fd = -1;
284
285 // Verify the data matches
286 T_EXPECT_EQ_STR(read_plist_vers, read_ios_support_version, "%s %s matches %s value", SYSTEM_VERSION_PLIST_PATH,
287 PRODUCT_VERSION_KEY, IOS_SUPPORT_VERSION_KEY);
288
289 T_PASS("verified version information with iOS shim enabled");
290
291#else // TARGET_OS_OSX
292 T_SKIP("iOS system version shim only supported on macOS");
293#endif // TARGET_OS_OSX
294}