xref: /xnu-12377.81.4/tests/arm_mte_knobs_policies.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2024 Apple Computer, 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 <arm_acle.h>
30 #include <darwintest.h>
31 #include <mach-o/dyld.h>
32 #include <mach/mach.h>
33 #include <spawn_private.h>
34 #include <stdlib.h>
35 #include <sys/spawn_internal.h>
36 
37 #include "arm_mte_utilities.h"
38 #include "test_utils.h"
39 
40 T_GLOBAL_META(T_META_NAMESPACE("xnu.arm.mte"),
41     T_META_RADAR_COMPONENT_NAME("Darwin Testing"),
42     T_META_RADAR_COMPONENT_VERSION("all"), T_META_OWNER("n_sabo"),
43     T_META_RUN_CONCURRENTLY(false),
44     T_META_IGNORECRASHES(".*knob.*"),
45     T_META_CHECK_LEAKS(false));
46 
47 static void
tag_violate_template(void)48 tag_violate_template(void)
49 {
50 	static const size_t ALLOC_SIZE = MTE_GRANULE_SIZE * 2;
51 
52 	vm_address_t address = 0;
53 	kern_return_t kr = vm_allocate(mach_task_self(), &address, ALLOC_SIZE, VM_FLAGS_ANYWHERE | VM_FLAGS_MTE);
54 	T_ASSERT_MACH_SUCCESS(kr, "allocate tagged memory");
55 	char *untagged_ptr = (char *) address;
56 
57 	char *orig_tagged_ptr = __arm_mte_get_tag(untagged_ptr);
58 	unsigned int orig_tag = extract_mte_tag(orig_tagged_ptr);
59 	T_ASSERT_EQ_UINT(orig_tag, 0U, "originally assigned tag is zero");
60 
61 	uint64_t mask = __arm_mte_exclude_tag(orig_tagged_ptr, 0);
62 	T_ASSERT_EQ_LLONG(mask, (1LL << 0), "zero tag is excluded");
63 
64 	char *random_tagged_ptr = NULL;
65 	for (unsigned int i = 0; i < NUM_MTE_TAGS * 4; i++) {
66 		random_tagged_ptr = __arm_mte_create_random_tag(untagged_ptr, mask);
67 		T_QUIET; T_ASSERT_NE_PTR(orig_tagged_ptr, random_tagged_ptr,
68 		    "random tag was not taken from excluded tag set");
69 
70 		ptrdiff_t diff = __arm_mte_ptrdiff(untagged_ptr, random_tagged_ptr);
71 		T_QUIET; T_ASSERT_EQ_ULONG(diff, (ptrdiff_t)0, "untagged %p and tagged %p have identical address bits",
72 		    untagged_ptr, random_tagged_ptr);
73 	}
74 
75 	__arm_mte_set_tag(random_tagged_ptr);
76 
77 	char *read_back = __arm_mte_get_tag(untagged_ptr);
78 	T_ASSERT_EQ_PTR(read_back, random_tagged_ptr, "tag was committed to memory correctly");
79 
80 	random_tagged_ptr[0] = 't';
81 	random_tagged_ptr[1] = 'e';
82 	random_tagged_ptr[2] = 's';
83 	random_tagged_ptr[3] = 't';
84 	T_EXPECT_EQ_STR(random_tagged_ptr, "test", "read/write from tagged memory");
85 
86 	void *next_granule_ptr = orig_tagged_ptr + MTE_GRANULE_SIZE;
87 	unsigned int next_granule_tag = extract_mte_tag(next_granule_ptr);
88 	T_QUIET; T_ASSERT_EQ_UINT(next_granule_tag, 0U,
89 	    "next MTE granule still has its originally assigned tag");
90 
91 	T_LOG("attempting out-of-bounds access to tagged memory");
92 	random_tagged_ptr[MTE_GRANULE_SIZE] = '!';
93 	T_LOG("bypass: survived OOB access");
94 
95 	/* We should not just have survived, but also re-issued the instruction */
96 	T_ASSERT_EQ_CHAR(random_tagged_ptr[MTE_GRANULE_SIZE], '!', "faulting instruction wasn't re-issued correctly");
97 
98 	__arm_mte_set_tag(orig_tagged_ptr);
99 	__arm_mte_set_tag(orig_tagged_ptr + MTE_GRANULE_SIZE);
100 	vm_deallocate(mach_task_self(), address, ALLOC_SIZE);
101 	exit(0);
102 }
103 
104 T_HELPER_DECL(mte_tag_violate, "helper to trigger an MTE violation")
105 {
106 	tag_violate_template();
107 }
108 
109 T_HELPER_DECL(mte_tag_violate_with_fork, "helper to trigger an MTE violation from a forked process")
110 {
111 	tag_violate_template();
112 	T_LOG("Knob enforced on main process\n");
113 	/* Now fork a child and verifying the knob was inherited */
114 	assert_normal_exit(^{
115 		tag_violate_template();
116 	}, "forked a child");
117 	T_LOG("Knob enforced on forked process\n");
118 }
119 
120 static void
default_tag_check_bypass_template(posix_spawn_secflag_options flags,bool expect_mte,bool should_kill_child,char * helper_name)121 default_tag_check_bypass_template(
122 	posix_spawn_secflag_options flags,
123 	bool expect_mte,
124 	bool should_kill_child,
125 	char *helper_name
126 	)
127 {
128 	char path[PATH_MAX];
129 	uint32_t path_size = sizeof(path);
130 	char *args[] = { path, "-n", helper_name, NULL};
131 	T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath");
132 	posix_spawn_with_flags_and_assert_successful_exit(args, flags, expect_mte, should_kill_child);
133 }
134 
135 T_DECL(test_posix_spawn_explicit_check_bypass_knob,
136     "Test MTE tag check bypass works with posix_spawnattr and flag POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS",
137     T_META_ENABLED(TARGET_CPU_ARM64),
138     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
139     XNU_T_META_SOC_SPECIFIC)
140 {
141 	default_tag_check_bypass_template(POSIX_SPAWN_SECFLAG_EXPLICIT_ENABLE | POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS, true, false, "mte_tag_violate");
142 }
143 
144 T_DECL(test_explicit_never_check_enable_with_bypass_knobs,
145     "Test that combining POSIX_SPAWN_SECFLAG_EXPLICIT_NEVER_CHECK_ENABLE &"
146     "POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS results in relaxed enforcement "
147     "on out of bounds memory access",
148     T_META_ENABLED(TARGET_CPU_ARM64),
149     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
150     XNU_T_META_SOC_SPECIFIC)
151 {
152 	default_tag_check_bypass_template(POSIX_SPAWN_SECFLAG_EXPLICIT_NEVER_CHECK_ENABLE | POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS, true, false, "mte_tag_violate");
153 }
154 
155 T_DECL(test_posix_spawn_secflag_explict_check_bypass_knob_inherited_on_fork,
156     "Test that POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS is inherited on fork",
157     T_META_ENABLED(TARGET_CPU_ARM64),
158     T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_MTE4", 1),
159     XNU_T_META_SOC_SPECIFIC)
160 {
161 	default_tag_check_bypass_template(POSIX_SPAWN_SECFLAG_EXPLICIT_CHECK_BYPASS, true, false, "mte_tag_violate_with_fork");
162 }
163