xref: /xnu-12377.61.12/tests/arm_mte_spawn_policies.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
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