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