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 #ifndef ARM_MTE_UTILITIES_H
30 #define ARM_MTE_UTILITIES_H
31
32 #include <mach/vm_types.h>
33 #include <sys/spawn_internal.h>
34
35 #include <stdbool.h>
36 #include <stdint.h>
37
38 #if (TARGET_OS_OSX || TARGET_OS_IOS) && defined(__arm64__)
39 // TODO(PT): It'd be nice to have this as an allow list rather than the inverse,
40 // but I wasn't able to restrict based on TARGET_OS_[IPHONE|IOS] as this is sometimes set even for XR_OS.
41 // For now, to keep things moving, just restrict this from being set on platforms where
42 // we know it's not the case.
43 #if !(TARGET_OS_XR || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_BRIDGE)
44 #define TARGET_SUPPORTS_MTE_EMULATION 1
45 #endif
46 #endif
47
48 #define MTE_TAG_SHIFT 56
49 #define MTE_TAG_MASK (0xFULL << MTE_TAG_SHIFT)
50 #define MTE_GRANULE_SIZE 16
51 #define VM_MIN_KERNEL_ADDRESS (0ULL - (2ULL << 40))
52 #define KERNEL_BUFFER_COPY_THRESHOLD (32 * 1024)
53 #define VIRTUAL_COPY_SZ ((32 * 1024) + 15)
54 #define NUM_MTE_TAGS 16
55 #define VM_WIMG_MTE 0x22 /* VM_MEM_MTE | VM_MEM_COHERENT */
56
57 // Used in arm_mte.c
58 #define SPAWN_HELPER_WITH_ENTITLEMENT "arm_mte_spawn_client_with_hardened_process_entitlement"
59 #define SPAWN_HELPER_WITHOUT_ENTITLEMENT "arm_mte_spawn_client_without_hardened_process_entitlement"
60
61 // Used in arm_mte_spawn_policies.c
62 #define HARDENED_PROCESS_HELPER SPAWN_HELPER_WITH_ENTITLEMENT
63 #define NO_HARDENED_PROCESS_HELPER SPAWN_HELPER_WITHOUT_ENTITLEMENT
64
65 #define HARDENED_PROCESS_TOP_LEVEL_ONLY_AND_IN_AMFI_MTE_OPT_OUT_HELPER \
66 "arm_mte_spawn_client_with_top_level_hardened_proc_entitlement_and_in_amfi_opt_out"
67
68 #define EXPECT_MTE "YES"
69 #define DO_NOT_EXPECT_MTE "NO"
70
71 static enum {
72 MTE_ENABLEMENT_TEST_HARDENED_PROCESS = 0x41,
73 MTE_ENABLEMENT_TEST_VANILLA_PROCESS,
74 MTE_ENABLEMENT_TEST_OPTED_OUT_PROCESS,
75 MTE_ENABLEMENT_TEST_DONE
76 } mte_enablement_test_op;
77
78 static char MTE_ENABLEMENT_TEST_HARDENED_PROCESS_STR[2] = {MTE_ENABLEMENT_TEST_HARDENED_PROCESS, '\x0'};
79 static char MTE_ENABLEMENT_TEST_VANILLA_PROCESS_STR[2] = {MTE_ENABLEMENT_TEST_VANILLA_PROCESS, '\x0'};
80 static char MTE_ENABLEMENT_TEST_OPTED_OUT_PROCESS_STR[2] = {MTE_ENABLEMENT_TEST_OPTED_OUT_PROCESS, '\x0'};
81 static char MTE_ENABLEMENT_TEST_DONE_STR[2] = {MTE_ENABLEMENT_TEST_DONE, '\x0'};
82
83 void kill_child(int child_pid);
84
85 bool wait_for_child(int pid);
86
87 bool validate_proc_pidinfo_mte_status(int child_pid,
88 bool expect_mte_enabled);
89 bool validate_proc_pidinfo_mte_soft_mode_status(int child_pid,
90 bool expect_mte_soft_mode_enabled);
91
92 bool fork_and_exec_new_process(char *new_argv[]);
93
94 static enum {
95 MTE_SPAWN_USE_VANILLA,
96 MTE_SPAWN_USE_LEGACY_API,
97 } mte_spawn_op;
98
99 bool posix_spawn_then_perform_action_from_process(char *new_argv[], uint8_t setup,
100 uint16_t spawn_flags);
101
102 inline unsigned int
extract_mte_tag(void * ptr)103 extract_mte_tag(void *ptr)
104 {
105 return (unsigned int)((((uintptr_t)ptr) & MTE_TAG_MASK) >> MTE_TAG_SHIFT);
106 }
107
108 int64_t run_sysctl_test(const char *t, int64_t value);
109
110 void expect_signal_impl(int signal, char *signal_name, void (^fn)(void), const char *msg);
111 #define expect_signal(signal, fn, msg) (expect_signal_impl((signal), #signal, (fn), (msg)))
112 void expect_sigkill(void (^fn)(void), const char *msg);
113
114 void expect_normal_exit(void (^fn)(void), const char *msg);
115 void assert_normal_exit(void (^fn)(void), const char *msg);
116
117 /*
118 * Uses vm_allocate to allocate `size` bytes of untagged memory and
119 * returns the untagged pointer to the caller.
120 *
121 * If mask is non-NULL, writes out a mask which can be passed in to
122 * __arm_mte_create_random_tag to exclude the zero tag.
123 */
124 void *allocate_untagged_memory(mach_vm_size_t size);
125
126 /*
127 * Uses allocate_tagged_memory to get a tagged memory range, and also applies
128 * a tag value to each of the MTE granules within the new allocation.
129 */
130 #define TAG_RANDOM_EXCLUDE(x) ((uintptr_t) ~x)
131 #define TAG_RANDOM TAG_RANDOM_EXCLUDE(0)
132 vm_address_t allocate_and_tag_range(mach_vm_size_t size, uintptr_t tag);
133 #endif
134
135 /* Posix spawn a process with any specified posix_spawn_secflag_options */
136 void
137 posix_spawn_with_flags_and_assert_successful_exit(
138 char *const*args,
139 posix_spawn_secflag_options flags,
140 bool expect_mte,
141 bool should_kill_child
142 );
143
144 /*
145 * Uses vm_allocate to allocate `size` bytes of tagged memory and
146 * returns the untagged pointer to the caller.
147 */
148 void *allocate_tagged_memory(mach_vm_size_t size, uint64_t *mask);
149
150 /* Utility to return the output of a output of a uint64_t sysctl */
151 uint64_t
152 sysctl_get_Q(const char *name);
153