1 /*
2 * Copyright (c) 2024 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <darwintest.h>
30 #include <stdbool.h>
31 #include <spawn_private.h>
32 #include <libproc.h>
33
34 #include "arm_mte_utilities.h"
35 #include "test_utils.h"
36
37 #if (TARGET_OS_OSX || TARGET_OS_IOS) && defined(__arm64__)
38 #if !(TARGET_OS_XR || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_BRIDGE)
39 #define TARGET_SUPPORTS_MTE_EMULATION 1
40 #endif
41 #endif
42
43 /*
44 * These tests verify whether the expected MTE state is found on target processes,
45 * exercising the various enablement rules and system APIs.
46 * Kernel behavior is extensively documented in kern_exec.c, but we recap here
47 * the key points:
48 *
49 * 1) MTE can be enabled on a target process, in order of preference, by inheritance,
50 * spawn flags and entitlements.
51 * 1.1) Inheritance can only be enabled via the dedicated POSIX_SPAWN_SECFLAG_EXPLICIT_ENABLE_INHERIT
52 * flag.
53 * 1.2) posix_spawn flags trump entitlements. Inheritance trumps posix_spawn flags.
54 * 1.3) entitlements are the desired and expected way to enable MTE in production. With the
55 * exception of launchd, no other entity on the system is expected to use posix_spawn
56 * as an enablement vector. (XCode uses it to provide a run-as-MTE feature)
57 *
58 * 2) posix_spawnattr_set_use_sec_transition_shims_np() API predates several of the rules at (1)
59 * and is therefore maintained in its legacy behavior of enabling both MTE and INHERITANCE
60 * by default. They both can be switched off via disablement flags: POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE
61 * and POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT.
62 *
63 * 3) POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE is _not_ supported on RELEASE to prevent attackers from
64 * using the posix_spawn API to disable MTE on a target. POSIX_SPAWN_SECFLAG_EXPLICIT_NEVER_CHECK_ENABLE,
65 * POSIX_SPAWN_SECFLAG_EXPLICIT_VM_POLICY_BYPASS and POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS share the
66 * same destiny.
67 *
68 * Testing goals:
69 * TG1 - ensure that, in absence of spawn flags, entitlements are respected downstream and no inheritance
70 * is present.
71 * TG2 - ensure that posix_spawnattr_set_use_sec_transition_shims_np() still respects legacy behavior.
72 * TG3 - ensure that posix_spawnattr_set_use_sec_transition_shims_np() is properly affected by
73 * POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE and POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT.
74 *
75 * <subject to rdar://145396237>
76 * TG4 - ensure that the direct manipulation API works as expected.
77 * <subject to having RELEASE behavior>
78 * TG5 - ensure that on RELEASE POSIX_SPAWN_SECFLAG_EXPLICIT_NEVER_CHECK_ENABLE,
79 * POSIX_SPAWN_SECFLAG_EXPLICIT_VM_POLICY_BYPASS and POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS are
80 * correctly ignored.
81 * TG6 - ensure that a first-party process signed with com.apple.developer.driverkit is
82 * sufficient for the system to apply MTE.
83 */
84
85 #define INITIAL_ITERATION "0"
86
87 T_GLOBAL_META(T_META_NAMESPACE("xnu.arm.mte"),
88 T_META_RADAR_COMPONENT_NAME("Darwin Testing"),
89 T_META_RADAR_COMPONENT_VERSION("all"), T_META_OWNER("n_sabo"),
90 T_META_RUN_CONCURRENTLY(false));
91
92 #define MTE_TOTAL_ENABLEMENT_TESTS 3
93 struct _mte_entitlement_process_expectation {
94 char *test_to_run;
95 char *expected_state;
96 char *test_name;
97 } mte_entitlement_process_expectation[MTE_TOTAL_ENABLEMENT_TESTS] = {
98 { MTE_ENABLEMENT_TEST_VANILLA_PROCESS_STR, DO_NOT_EXPECT_MTE, "vanilla" },
99 { MTE_ENABLEMENT_TEST_HARDENED_PROCESS_STR, EXPECT_MTE, "hardened-process"},
100 { MTE_ENABLEMENT_TEST_OPTED_OUT_PROCESS_STR, DO_NOT_EXPECT_MTE, "AMFI opt-out"},
101 };
102
103 static void
do_entitlement_test(char * binary_to_launch,char * expected_mte_state)104 do_entitlement_test(char *binary_to_launch, char *expected_mte_state)
105 {
106 for (int i = 0; i < MTE_TOTAL_ENABLEMENT_TESTS; i++) {
107 T_LOG("Running %s that will spawn %s\n", binary_to_launch, mte_entitlement_process_expectation[i].test_name);
108 char *test_argv[] = {
109 binary_to_launch,
110 expected_mte_state,
111 mte_entitlement_process_expectation[i].test_to_run,
112 mte_entitlement_process_expectation[i].expected_state,
113 NULL
114 };
115
116 bool test_succeeded = fork_and_exec_new_process(test_argv);
117 T_ASSERT_TRUE(test_succeeded, "fork/exec entitlement test");
118
119 test_succeeded = posix_spawn_then_perform_action_from_process(test_argv, MTE_SPAWN_USE_VANILLA, 0);
120 T_ASSERT_TRUE(test_succeeded, "vanilla posix_spawn + entitlements test");
121 }
122 }
123
124 static void
do_spawn_flags_test(char * binary_to_launch,char * expected_mte_state,char * test_to_perform,char * expected_next_test_mte_state,uint16_t sec_flags)125 do_spawn_flags_test(
126 char *binary_to_launch,
127 char *expected_mte_state,
128 char *test_to_perform,
129 char *expected_next_test_mte_state,
130 uint16_t sec_flags)
131 {
132 char *test_argv[] = {
133 binary_to_launch,
134 expected_mte_state,
135 test_to_perform,
136 expected_next_test_mte_state,
137 NULL
138 };
139
140 bool test_succeeded = posix_spawn_then_perform_action_from_process(test_argv, MTE_SPAWN_USE_LEGACY_API, sec_flags);
141 T_ASSERT_TRUE(test_succeeded, "vanilla posix_spawn + entitlements test");
142 }
143
144 /*
145 * TG1.
146 *
147 * Start different entitled processes that execute the whole spectrum of entitlement possibilities in a process
148 * tree and ensure that expectations are matched.
149 */
150
151 T_DECL(non_mte_enabled_binary_enablement_test,
152 "Verify enablement rules against a process tree that starts with "
153 "a non-MTE enabled binary",
154 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
155 XNU_T_META_SOC_SPECIFIC) {
156 do_entitlement_test(SPAWN_HELPER_WITHOUT_ENTITLEMENT, DO_NOT_EXPECT_MTE);
157 }
158
159 T_DECL(mte_enabled_binary_enablement_test,
160 "Verify enablement rules against a process tree that starts with "
161 "a MTE-enabled binary.",
162 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
163 XNU_T_META_SOC_SPECIFIC) {
164 do_entitlement_test(SPAWN_HELPER_WITH_ENTITLEMENT, EXPECT_MTE);
165 }
166
167 T_DECL(mte_opted_out_binary_enablement_test,
168 "Verify enablement rules against a process tree that starts with "
169 "a MTE-opted-out binary.",
170 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
171 XNU_T_META_SOC_SPECIFIC) {
172 T_SKIP("skip until Monorail doesn't resign binaries fooling our ID checks");
173 do_entitlement_test(HARDENED_PROCESS_TOP_LEVEL_ONLY_AND_IN_AMFI_MTE_OPT_OUT_HELPER, DO_NOT_EXPECT_MTE);
174 }
175
176 /*
177 * TG2.
178 *
179 * Verify that posix_spawnattr_set_use_sec_transition_shims_np() still maintains the original
180 * behavior of enabling MTE and enabling inheritance whenever invoked without flags.
181 */
182 T_DECL(mte_legacy_spawn_api_default_behavior,
183 "Call posix_spawnattr_set_use_sec_transition_shims_np() and verify that "
184 "MTE is enabled AND inheritance is present.",
185 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
186 XNU_T_META_SOC_SPECIFIC) {
187 /* spawn flags and inheritance take precedence over the entitlement state */
188 for (int i = 0; i < MTE_TOTAL_ENABLEMENT_TESTS; i++) {
189 do_spawn_flags_test(SPAWN_HELPER_WITHOUT_ENTITLEMENT, EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
190 EXPECT_MTE, 0);
191 do_spawn_flags_test(SPAWN_HELPER_WITH_ENTITLEMENT, EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
192 EXPECT_MTE, 0);
193 #if MONORAIL_DOESNT_RESIGN
194 do_spawn_flags_test(HARDENED_PROCESS_TOP_LEVEL_ONLY_AND_IN_AMFI_MTE_OPT_OUT_HELPER, EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
195 EXPECT_MTE, 0);
196 #endif /* MONORAIL_DOESNT_RESIGN */
197 }
198 }
199
200 /*
201 * TG3.
202 *
203 * Verify that posix_spawnattr_set_use_sec_transition_shims_np() correctly handles
204 * POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE and POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT
205 * for internal usecases.
206 */
207 T_DECL(mte_legacy_spawn_api_disable_flag_development,
208 "Call posix_spawnattr_set_use_sec_transition_shims_np() passing the"
209 "POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE flag and verify that MTE is disabled.",
210 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
211 XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL,
212 XNU_T_META_SOC_SPECIFIC) {
213 uint16_t sec_flags = POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE;
214
215 T_LOG("posix_spawnattr_set_use_sec_transition_shims_np(POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE)\n");
216
217 /* posix_spawnattr_set_use_sec_transition_shims_np(POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE) implies inheritance */
218 for (int i = 0; i < MTE_TOTAL_ENABLEMENT_TESTS; i++) {
219 do_spawn_flags_test(SPAWN_HELPER_WITHOUT_ENTITLEMENT, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
220 DO_NOT_EXPECT_MTE, sec_flags);
221 do_spawn_flags_test(SPAWN_HELPER_WITH_ENTITLEMENT, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
222 DO_NOT_EXPECT_MTE, sec_flags);
223 #if MONORAIL_DOESNT_RESIGN
224 do_spawn_flags_test(HARDENED_PROCESS_TOP_LEVEL_ONLY_AND_IN_AMFI_MTE_OPT_OUT_HELPER, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
225 DO_NOT_EXPECT_MTE, sec_flags);
226 #endif /* MONORAIL_DOESNT_RESIGN */
227 }
228
229 /* Now with inheritance disabled. */
230 T_LOG("posix_spawnattr_set_use_sec_transition_shims_np(POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT|EXPLICIT_DISABLE)\n");
231 sec_flags = POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE_INHERIT | POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE;
232 for (int i = 0; i < MTE_TOTAL_ENABLEMENT_TESTS; i++) {
233 do_spawn_flags_test(SPAWN_HELPER_WITHOUT_ENTITLEMENT, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
234 mte_entitlement_process_expectation[i].expected_state, sec_flags);
235 do_spawn_flags_test(SPAWN_HELPER_WITH_ENTITLEMENT, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
236 mte_entitlement_process_expectation[i].expected_state, sec_flags);
237 #if MONORAIL_DOESNT_RESIGN
238 do_spawn_flags_test(HARDENED_PROCESS_TOP_LEVEL_ONLY_AND_IN_AMFI_MTE_OPT_OUT_HELPER, DO_NOT_EXPECT_MTE, mte_entitlement_process_expectation[i].test_to_run,
239 mte_entitlement_process_expectation[i].expected_state, sec_flags);
240 #endif /* MONORAIL_DOESNT_RESIGN */
241 }
242 }
243
244 T_DECL(mte_legacy_spawn_api_disable_flag_release,
245 "Call posix_spawnattr_set_use_sec_transition_shims_np() passing the"
246 "POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE flag and verify that on RELEASE we fail the call.",
247 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
248 XNU_T_META_REQUIRES_RELEASE_KERNEL,
249 XNU_T_META_SOC_SPECIFIC) {
250 posix_spawnattr_t attr;
251 pid_t child_pid = 0;
252 errno_t ret = posix_spawnattr_init(&attr);
253 /* We should not get to execute the binary at all, so no need to have the right arguments. */
254 char *args[] = { SPAWN_HELPER_WITH_ENTITLEMENT, NULL};
255 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init");
256
257 ret = posix_spawnattr_set_use_sec_transition_shims_np(&attr, POSIX_SPAWN_SECFLAG_EXPLICIT_DISABLE);
258 T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_set_use_sec_transition_shims_np");
259
260 ret = posix_spawn(&child_pid, args[0], NULL, &attr, args, NULL);
261 T_ASSERT_POSIX_FAILURE(ret, EINVAL, "posix_spawn DISABLE on RELEASE");
262 }
263
264 /*
265 * TG6.
266 *
267 * Verify that a first-party dext will get MTE out of the box.
268 */
269 T_DECL(first_party_dext_spawns_with_mte,
270 "Ensure first-party dexts receive MTE",
271 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
272 XNU_T_META_SOC_SPECIFIC) {
273 /* Given a first-party binary signed with com.apple.developer.driverkit */
274 pid_t target_pid;
275 char* target_argv[] = {"arm_mte_driverkit_standin", NULL};
276
277 /* When the binary is spawned */
278 int ret = posix_spawn(&target_pid, target_argv[0], NULL, NULL, target_argv, NULL);
279 T_ASSERT_POSIX_ZERO(ret, "posix_spawn(%s)", target_argv[0]);
280 T_ASSERT_NE(target_pid, 0, "posix_spawn(%s)", target_argv[0]);
281
282 /* And we interrogate its MTE state */
283 struct proc_bsdinfowithuniqid info;
284 ret = proc_pidinfo(target_pid, PROC_PIDT_BSDINFOWITHUNIQID, 1, &info,
285 PROC_PIDT_BSDINFOWITHUNIQID_SIZE);
286 T_ASSERT_EQ(ret, (int)sizeof(info), "proc_pidinfo");
287 bool is_proc_mte_enabled = (info.pbsd.pbi_flags & PROC_FLAG_SEC_ENABLED) != 0;
288
289 /* Then we observe that the process is MTE-enabled, despite us not doing anything special */
290 T_ASSERT_TRUE(is_proc_mte_enabled, "Expected 1p dexts to be MTE-enabled by default");
291 }
292
293 T_DECL(mte_double_entitlement_setting_failure,
294 "Execute a binary which has both the com.apple.developer and com.apple.security"
295 " set of entitlements and verify that we fail execution.",
296 T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
297 XNU_T_META_SOC_SPECIFIC) {
298 pid_t child_pid = 0;
299 /* We should not get to execute the binary at all, so no need to have the right arguments. */
300 char *args[] = { "arm_mte_spawn_client_with_invalid_entitlement_setting", NULL};
301
302 int ret = posix_spawn(&child_pid, args[0], NULL, NULL, args, NULL);
303 T_ASSERT_NE(0, ret, "poisx_spawn with double entitlement must fail");
304 }
305