1 /*
2 * Copyright (c) 2023 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 /*
30 * File: vm/vm_sanitize_telemetry.c
31 *
32 * Telemetry for VM functions to detect issues and risks as part of VM API
33 * hygiene work.
34 */
35
36 /*
37 * Implementation relies on CoreAnalytics for telemetry infrastructure.
38 * It relies on telemetry.c for getting and encoding UUIDs and slides.
39 */
40
41 #include <kern/backtrace.h>
42 #include <kern/telemetry.h>
43 #include <libkern/coreanalytics/coreanalytics.h>
44 #include <mach/resource_monitors.h>
45 #include <mach/sdt.h>
46 #include <vm/vm_log.h>
47 #include <sys/kdebug_triage.h>
48 #include <vm/vm_sanitize_telemetry.h>
49 #include <vm/vm_protos.h>
50
51 #pragma mark KTriage strings
52
53 static const char *vm_sanitize_triage_strings[] =
54 {
55 [KDBG_TRIAGE_VM_SANITIZE_PREFIX] = "VM API - Unsanitary input passed to ",
56 [KDBG_TRIAGE_VM_SANITIZE_MACH_MAKE_MEMORY_ENTRY] = "mach_make_memory_entry\n",
57 [KDBG_TRIAGE_VM_SANITIZE_MACH_MEMORY_ENTRY_PAGE_OP] = "mach_memory_entry_page_op\n",
58 [KDBG_TRIAGE_VM_SANITIZE_MACH_MEMORY_ENTRY_RANGE_OP] = "mach_memory_entry_range_op\n",
59 [KDBG_TRIAGE_VM_SANITIZE_MACH_MEMORY_ENTRY_MAP_SIZE] = "mach_memory_entry_map_size\n",
60 [KDBG_TRIAGE_VM_SANITIZE_MACH_MEMORY_OBJECT_MEMORY_ENTRY] = "mach_memory_object_memory_entry\n",
61 [KDBG_TRIAGE_VM_SANITIZE_VM_ALLOCATE_FIXED] = "vm_allocate(VM_FLAGS_FIXED)\n",
62 [KDBG_TRIAGE_VM_SANITIZE_VM_ALLOCATE_ANYWHERE] = "vm_allocate(VM_FLAGS_ANYWHERE)\n",
63 [KDBG_TRIAGE_VM_SANITIZE_VM_DEALLOCATE] = "vm_deallocate\n",
64 [KDBG_TRIAGE_VM_SANITIZE_MUNMAP] = "munmap\n",
65 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_REMAP] = "vm_remap or vm_remap_new\n",
66 [KDBG_TRIAGE_VM_SANITIZE_MMAP] = "mmap\n",
67 [KDBG_TRIAGE_VM_SANITIZE_MAP_WITH_LINKING_NP] = "map_with_linking_np\n",
68 [KDBG_TRIAGE_VM_SANITIZE_ENTER_MEM_OBJ] = "vm_map\n",
69 [KDBG_TRIAGE_VM_SANITIZE_ENTER_MEM_OBJ_CTL] = "vm_map\n",
70 [KDBG_TRIAGE_VM_SANITIZE_MREMAP_ENCRYPTED] = "mremap_encrypted\n",
71 [KDBG_TRIAGE_VM_SANITIZE_VM_WIRE_USER] = "vm_wire\n",
72 [KDBG_TRIAGE_VM_SANITIZE_VM_UNWIRE_USER] = "vm_wire\n",
73 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_WIRE] = "vm_map_wire\n",
74 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_UNWIRE] = "vm_map_unwire\n",
75 [KDBG_TRIAGE_VM_SANITIZE_VSLOCK] = "vslock\n",
76 [KDBG_TRIAGE_VM_SANITIZE_VSUNLOCK] = "vsunlock\n",
77 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_COPY_OVERWRITE] = "vm_map_copy_overwrite\n",
78 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_COPYIN] = "vm_map_copyin\n",
79 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_READ_USER] = "vm_read\n",
80 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_WRITE_USER] = "vm_write\n",
81 [KDBG_TRIAGE_VM_SANITIZE_MACH_VM_INHERIT] = "mach_vm_inherit\n",
82 [KDBG_TRIAGE_VM_SANITIZE_VM_INHERIT] = "vm_inherit\n",
83 [KDBG_TRIAGE_VM_SANITIZE_VM32_INHERIT] = "vm32_inherit\n",
84 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_INHERIT] = "vm_map_inherit\n",
85 [KDBG_TRIAGE_VM_SANITIZE_MINHERIT] = "minherit\n",
86 [KDBG_TRIAGE_VM_SANITIZE_MACH_VM_PROTECT] = "mach_vm_protect\n",
87 [KDBG_TRIAGE_VM_SANITIZE_VM_PROTECT] = "vm_protect\n",
88 [KDBG_TRIAGE_VM_SANITIZE_VM32_PROTECT] = "vm32_protect\n",
89 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_PROTECT] = "vm_map_protect\n",
90 [KDBG_TRIAGE_VM_SANITIZE_MPROTECT] = "mprotect\n",
91 [KDBG_TRIAGE_VM_SANITIZE_USERACC] = "useracc\n",
92 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_MSYNC] = "vm_map_msync\n",
93 [KDBG_TRIAGE_VM_SANITIZE_MSYNC] = "msync\n",
94 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_MACHINE_ATTRIBUTE] = "vm_map_machine_attribute\n",
95 [KDBG_TRIAGE_VM_SANITIZE_MINCORE] = "mincore\n",
96 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_PAGE_RANGE_INFO] = "vm_map_page_range_info\n",
97 [KDBG_TRIAGE_VM_SANITIZE_VM_MAP_PAGE_RANGE_QUERY] = "vm_map_page_range_query\n",
98 [KDBG_TRIAGE_VM_SANITIZE_VM_BEHAVIOR_SET] = "vm_behavior_set\n",
99 [KDBG_TRIAGE_VM_SANITIZE_MADVISE] = "madvise\n",
100 [KDBG_TRIAGE_VM_SANITIZE_MACH_VM_DEFERRED_RECLAMATION_BUFFER_INIT] = "mach_vm_deferred_reclamation_buffer_init\n",
101 [KDBG_TRIAGE_VM_SANITIZE_MACH_VM_RANGE_CREATE] = "mach_vm_range_create\n",
102 [KDBG_TRIAGE_VM_SANITIZE_SHARED_REGION_MAP_AND_SLIDE_2_NP] = "shared_region_map_and_slide_2_np\n",
103 [KDBG_TRIAGE_VM_SANITIZE_TEST] = "vm_sanitize_run_test\n",
104 };
105
106 static ktriage_strings_t ktriage_vm_sanitize_subsystem_strings = {VM_SANITIZE_MAX_TRIAGE_STRINGS, vm_sanitize_triage_strings};
107
108
109 #pragma mark Lengths for CA event fields
110
111 #define VM_SANITIZE_BACKTRACE_FRAME_COUNT (10)
112 #define CA_VM_PROCESS_NAME_LEN 33
113
114 static_assert(CA_VM_PROCESS_NAME_LEN == (2 * MAXCOMLEN + 1));
115
116 #define CA_VM_BACKTRACE_AND_SYM_LEN 340
117
118 #define CA_VM_BACKTRACE_BYTES_PER_FRAME (8)
119 #define CA_VM_SYM_INFO_ENTRY_LEN (52)
120 #define CA_VM_MAX_EXPECTED_KEXTS (4)
121
122 #define CA_VM_BT_LEN (VM_SANITIZE_BACKTRACE_FRAME_COUNT * CA_VM_BACKTRACE_BYTES_PER_FRAME)
123 #define CA_VM_KERNEL_UUID_DATA_LEN (CA_VM_SYM_INFO_ENTRY_LEN)
124 #define CA_VM_KEXT_UUID_DATA_LEN (CA_VM_MAX_EXPECTED_KEXTS * CA_VM_SYM_INFO_ENTRY_LEN)
125
126 static_assert(CA_VM_BACKTRACE_AND_SYM_LEN == CA_VM_BT_LEN + CA_VM_KERNEL_UUID_DATA_LEN + CA_VM_KEXT_UUID_DATA_LEN);
127
128 #pragma mark Packing for method and checker
129
130 /*
131 *
132 * 63 60 59 40 39 20 19 0
133 * +----------+-----------+-----------+---------+
134 * | reserved |checker cnt| checker | method |
135 * +----------+-----------+-----------+---------+
136 */
137
138 #define CA_VM_PACKING_METHOD_OFFSET (0)
139 #define CA_VM_PACKING_CHECKER_OFFSET (20)
140 #define CA_VM_PACKING_CHECKER_COUNT_OFFSET (40)
141
142 #define CA_VM_PACK(method, checker, checker_count) ( \
143 ((method) << CA_VM_PACKING_METHOD_OFFSET) \
144 | ((checker) << CA_VM_PACKING_CHECKER_OFFSET) \
145 | ((checker_count) << CA_VM_PACKING_CHECKER_COUNT_OFFSET))
146
147 #pragma mark Extern declarations for BSD functions
148
149 extern const char *proc_best_name(struct proc *proc);
150 extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
151
152 #pragma mark Event declaration
153
154 /*
155 * DO NOT change the event name, or we will stop getting telemetry.
156 * DO NOT rename fields, or that data will be lost.
157 * Ideally, DO NOT change this event at all.
158 */
159 CA_EVENT(vm_sanitize_updated_return_code,
160 CA_STATIC_STRING(CA_VM_BACKTRACE_AND_SYM_LEN), backtrace,
161 CA_STATIC_STRING(CA_VM_PROCESS_NAME_LEN), process_name,
162 CA_STATIC_STRING(CA_UUID_LEN), process_uuid,
163 CA_INT, method_checker_info,
164 CA_INT, arg1,
165 CA_INT, arg2,
166 CA_INT, arg3,
167 CA_INT, arg4,
168 CA_INT, future_ret,
169 CA_INT, past_ret);
170
171 #pragma mark Globals
172
173 #if !(DEBUG || DEVELOPMENT)
174 static const
175 #endif
176 // Set by a sysctl to disable telemetry while tests are running.
177 uint32_t disable_vm_sanitize_telemetry = 0;
178
179 #pragma mark CoreAnalytics wrapper implementation
180
181 static void
vm_sanitize_populate_symbolicatable_backtrace_string(uintptr_t addr,char * buffer,size_t buf_size)182 vm_sanitize_populate_symbolicatable_backtrace_string(uintptr_t addr, char *buffer, size_t buf_size)
183 {
184 uintptr_t frames[VM_SANITIZE_BACKTRACE_FRAME_COUNT];
185 struct backtrace_control ctl = BTCTL_INIT;
186 ctl.btc_frame_addr = addr;
187 backtrace_info_t info = 0;
188 int backtrace_count = backtrace(frames, VM_SANITIZE_BACKTRACE_FRAME_COUNT, &ctl, &info);
189 telemetry_backtrace_to_string(buffer, buf_size, backtrace_count, frames);
190 }
191
192 static void
vm_sanitize_populate_process_name(struct proc * proc,char buffer[static CA_VM_PROCESS_NAME_LEN])193 vm_sanitize_populate_process_name(struct proc *proc, char buffer[static CA_VM_PROCESS_NAME_LEN])
194 {
195 const char *proc_name = proc_best_name(proc);
196 strlcpy(buffer, proc_name, CA_VM_PROCESS_NAME_LEN);
197 }
198
199 static void
vm_sanitize_populate_process_uuid(struct proc * proc,char buffer[static sizeof (uuid_string_t)])200 vm_sanitize_populate_process_uuid(struct proc *proc, char buffer[static sizeof(uuid_string_t)])
201 {
202 uuid_t parsed_uuid = { 0 };
203 proc_getexecutableuuid(proc, parsed_uuid, sizeof(parsed_uuid));
204 uuid_unparse(parsed_uuid, buffer);
205 }
206
207 static void
vm_sanitize_send_telemetry_core_analytics(vm_sanitize_method_t method,vm_sanitize_checker_t checker,vm_sanitize_checker_count_t checker_count,uint64_t arg1,uint64_t arg2,uint64_t arg3,uint64_t arg4,uint64_t future_ret,uint64_t past_ret)208 vm_sanitize_send_telemetry_core_analytics(
209 vm_sanitize_method_t method,
210 vm_sanitize_checker_t checker,
211 vm_sanitize_checker_count_t checker_count,
212 uint64_t arg1,
213 uint64_t arg2,
214 uint64_t arg3,
215 uint64_t arg4,
216 uint64_t future_ret,
217 uint64_t past_ret)
218 {
219 struct proc *proc = current_proc();
220 ca_event_t ca_event = CA_EVENT_ALLOCATE_FLAGS(vm_sanitize_updated_return_code, Z_NOWAIT | Z_ZERO);
221 if (NULL == ca_event) {
222 vm_log_error("Failed to allocate event for VM API telemetry.");
223 return;
224 }
225 CA_EVENT_TYPE(vm_sanitize_updated_return_code) * event_data = ca_event->data;
226
227 vm_sanitize_populate_symbolicatable_backtrace_string((uintptr_t)__builtin_frame_address(0), event_data->backtrace, sizeof(event_data->backtrace));
228
229 vm_sanitize_populate_process_name(proc, event_data->process_name);
230
231 vm_sanitize_populate_process_uuid(proc, event_data->process_uuid);
232
233 event_data->method_checker_info = CA_VM_PACK(method, checker, checker_count);
234 event_data->arg1 = arg1;
235 event_data->arg2 = arg2;
236 event_data->arg3 = arg3;
237 event_data->arg4 = arg4;
238 event_data->future_ret = future_ret;
239 event_data->past_ret = past_ret;
240
241 CA_EVENT_SEND(ca_event);
242 return;
243 }
244
245 #pragma mark KTriage wrapper implementation
246
247 static bool ktriage_initialized = false;
248
249 static void
vm_sanitize_initialize_ktriage()250 vm_sanitize_initialize_ktriage()
251 {
252 if (ktriage_initialized) {
253 return;
254 }
255 if (__improbable(0 != ktriage_register_subsystem_strings(
256 KDBG_TRIAGE_SUBSYS_VM_SANITIZE,
257 &ktriage_vm_sanitize_subsystem_strings))) {
258 panic("Failed to set up ktriage for VM sanitization.");
259 }
260 ktriage_initialized = true;
261 }
262
263 static void
vm_sanitize_ktriage_record(enum vm_sanitize_subsys_error_codes ktriage_code,uint64_t past_ret)264 vm_sanitize_ktriage_record(
265 enum vm_sanitize_subsys_error_codes ktriage_code,
266 uint64_t past_ret)
267 {
268 vm_sanitize_initialize_ktriage();
269 if (ktriage_code != KDBG_TRIAGE_VM_SANITIZE_SKIP) {
270 ktriage_record(thread_tid(current_thread()),
271 KDBG_TRIAGE_EVENTID(
272 KDBG_TRIAGE_SUBSYS_VM_SANITIZE,
273 KDBG_TRIAGE_RESERVED,
274 ktriage_code),
275 past_ret /* arg */);
276 }
277 }
278
279 #pragma mark Entry point implementation
280
281 void
vm_sanitize_send_telemetry(vm_sanitize_method_t method,vm_sanitize_checker_t checker,vm_sanitize_checker_count_t checker_count,enum vm_sanitize_subsys_error_codes ktriage_code,uint64_t arg1,uint64_t arg2,uint64_t arg3,uint64_t arg4,uint64_t future_ret,uint64_t past_ret)282 vm_sanitize_send_telemetry(
283 vm_sanitize_method_t method,
284 vm_sanitize_checker_t checker,
285 vm_sanitize_checker_count_t checker_count,
286 enum vm_sanitize_subsys_error_codes ktriage_code,
287 uint64_t arg1,
288 uint64_t arg2,
289 uint64_t arg3,
290 uint64_t arg4,
291 uint64_t future_ret,
292 uint64_t past_ret)
293 {
294 if (0 == disable_vm_sanitize_telemetry) {
295 vm_sanitize_send_telemetry_core_analytics(
296 method, checker, checker_count,
297 arg1, arg2, arg3, arg4,
298 future_ret, past_ret);
299 }
300
301 DTRACE_VM7(vm_sanitize,
302 uint64_t, CA_VM_PACK(method, checker, checker_count),
303 uint64_t, arg1,
304 uint64_t, arg2,
305 uint64_t, arg3,
306 uint64_t, arg4,
307 uint64_t, future_ret,
308 uint64_t, past_ret);
309
310 vm_sanitize_ktriage_record(ktriage_code, past_ret);
311
312 return;
313 }
314