xref: /xnu-11215.1.10/osfmk/vm/vm_sanitize_telemetry.c (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
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 <os/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 Lengths for CA event fields
52 
53 #define VM_SANITIZE_BACKTRACE_FRAME_COUNT (10)
54 #define CA_VM_PROCESS_NAME_LEN 33
55 
56 static_assert(CA_VM_PROCESS_NAME_LEN == (2 * MAXCOMLEN + 1));
57 
58 #define CA_VM_BACKTRACE_AND_SYM_LEN 340
59 
60 #define CA_VM_BACKTRACE_BYTES_PER_FRAME (8)
61 #define CA_VM_SYM_INFO_ENTRY_LEN (52)
62 #define CA_VM_MAX_EXPECTED_KEXTS (4)
63 
64 #define CA_VM_BT_LEN (VM_SANITIZE_BACKTRACE_FRAME_COUNT * CA_VM_BACKTRACE_BYTES_PER_FRAME)
65 #define CA_VM_KERNEL_UUID_DATA_LEN (CA_VM_SYM_INFO_ENTRY_LEN)
66 #define CA_VM_KEXT_UUID_DATA_LEN (CA_VM_MAX_EXPECTED_KEXTS * CA_VM_SYM_INFO_ENTRY_LEN)
67 
68 static_assert(CA_VM_BACKTRACE_AND_SYM_LEN == CA_VM_BT_LEN + CA_VM_KERNEL_UUID_DATA_LEN + CA_VM_KEXT_UUID_DATA_LEN);
69 
70 #pragma mark Packing for method and checker
71 
72 /*
73  *
74  * 63       60 59       40 39       20 19       0
75  * +----------+-----------+-----------+---------+
76  * | reserved |checker cnt|  checker  |  method |
77  * +----------+-----------+-----------+---------+
78  */
79 
80 #define CA_VM_PACKING_METHOD_OFFSET (0)
81 #define CA_VM_PACKING_CHECKER_OFFSET (20)
82 #define CA_VM_PACKING_CHECKER_COUNT_OFFSET (40)
83 
84 #define CA_VM_PACK(method, checker, checker_count) (               \
85 	((method) << CA_VM_PACKING_METHOD_OFFSET)                  \
86 	| ((checker) << CA_VM_PACKING_CHECKER_OFFSET)              \
87 	| ((checker_count) << CA_VM_PACKING_CHECKER_COUNT_OFFSET))
88 
89 #pragma mark Extern declarations for BSD functions
90 
91 extern const char *proc_best_name(struct proc *proc);
92 extern void proc_getexecutableuuid(void *p, unsigned char *uuidbuf, unsigned long size);
93 
94 #pragma mark Event declaration
95 
96 CA_EVENT(vm_sanitize_updated_return_code,
97     CA_STATIC_STRING(CA_VM_BACKTRACE_AND_SYM_LEN), backtrace,
98     CA_STATIC_STRING(CA_VM_PROCESS_NAME_LEN), process_name,
99     CA_STATIC_STRING(CA_UUID_LEN), process_uuid,
100     CA_INT, method_checker_info,
101     CA_INT, arg1,
102     CA_INT, arg2,
103     CA_INT, arg3,
104     CA_INT, arg4,
105     CA_INT, future_ret,
106     CA_INT, past_ret);
107 
108 #pragma mark Globals
109 
110 #if !(DEBUG || DEVELOPMENT)
111 static const
112 #endif
113 // Set by a sysctl to disable telemetry while tests are running.
114 uint32_t disable_vm_sanitize_telemetry = 0;
115 
116 #pragma mark Implementation
117 
118 static void
vm_sanitize_populate_symbolicatable_backtrace_string(uintptr_t addr,char * buffer,size_t buf_size)119 vm_sanitize_populate_symbolicatable_backtrace_string(uintptr_t addr, char *buffer, size_t buf_size)
120 {
121 	uintptr_t frames[VM_SANITIZE_BACKTRACE_FRAME_COUNT];
122 	struct backtrace_control ctl = BTCTL_INIT;
123 	ctl.btc_frame_addr = addr;
124 	backtrace_info_t info = 0;
125 	int backtrace_count = backtrace(frames, VM_SANITIZE_BACKTRACE_FRAME_COUNT, &ctl, &info);
126 	telemetry_backtrace_to_string(buffer, buf_size, backtrace_count, frames);
127 }
128 
129 static void
vm_sanitize_populate_process_name(struct proc * proc,char buffer[static CA_VM_PROCESS_NAME_LEN])130 vm_sanitize_populate_process_name(struct proc *proc, char buffer[static CA_VM_PROCESS_NAME_LEN])
131 {
132 	const char *proc_name = proc_best_name(proc);
133 	strlcpy(buffer, proc_name, CA_VM_PROCESS_NAME_LEN);
134 }
135 
136 static void
vm_sanitize_populate_process_uuid(struct proc * proc,char buffer[static sizeof (uuid_string_t)])137 vm_sanitize_populate_process_uuid(struct proc *proc, char buffer[static sizeof(uuid_string_t)])
138 {
139 	uuid_t parsed_uuid = { 0 };
140 	proc_getexecutableuuid(proc, parsed_uuid, sizeof(parsed_uuid));
141 	uuid_unparse(parsed_uuid, buffer);
142 }
143 
144 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)145 vm_sanitize_send_telemetry_core_analytics(
146 	vm_sanitize_method_t method,
147 	vm_sanitize_checker_t checker,
148 	vm_sanitize_checker_count_t checker_count,
149 	uint64_t arg1,
150 	uint64_t arg2,
151 	uint64_t arg3,
152 	uint64_t arg4,
153 	uint64_t future_ret,
154 	uint64_t past_ret)
155 {
156 	struct proc *proc = current_proc();
157 	ca_event_t ca_event = CA_EVENT_ALLOCATE_FLAGS(vm_sanitize_updated_return_code, Z_NOWAIT | Z_ZERO);
158 	if (NULL == ca_event) {
159 		os_log_error(OS_LOG_DEFAULT, "Failed to allocate event for VM API telemetry.");
160 		return;
161 	}
162 	CA_EVENT_TYPE(vm_sanitize_updated_return_code) * event_data = ca_event->data;
163 
164 	vm_sanitize_populate_symbolicatable_backtrace_string((uintptr_t)__builtin_frame_address(0), event_data->backtrace, sizeof(event_data->backtrace));
165 
166 	vm_sanitize_populate_process_name(proc, event_data->process_name);
167 
168 	vm_sanitize_populate_process_uuid(proc, event_data->process_uuid);
169 
170 	event_data->method_checker_info = CA_VM_PACK(method, checker, checker_count);
171 	event_data->arg1 = arg1;
172 	event_data->arg2 = arg2;
173 	event_data->arg3 = arg3;
174 	event_data->arg4 = arg4;
175 	event_data->future_ret = future_ret;
176 	event_data->past_ret = past_ret;
177 
178 	CA_EVENT_SEND(ca_event);
179 	return;
180 }
181 
182 
183 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)184 vm_sanitize_send_telemetry(
185 	vm_sanitize_method_t method,
186 	vm_sanitize_checker_t checker,
187 	vm_sanitize_checker_count_t checker_count,
188 	enum vm_sanitize_subsys_error_codes ktriage_code,
189 	uint64_t arg1,
190 	uint64_t arg2,
191 	uint64_t arg3,
192 	uint64_t arg4,
193 	uint64_t future_ret,
194 	uint64_t past_ret)
195 {
196 	if (0 == disable_vm_sanitize_telemetry) {
197 		vm_sanitize_send_telemetry_core_analytics(
198 			method, checker, checker_count,
199 			arg1, arg2, arg3, arg4,
200 			future_ret, past_ret);
201 	}
202 
203 	DTRACE_VM7(vm_sanitize,
204 	    uint64_t, CA_VM_PACK(method, checker, checker_count),
205 	    uint64_t, arg1,
206 	    uint64_t, arg2,
207 	    uint64_t, arg3,
208 	    uint64_t, arg4,
209 	    uint64_t, future_ret,
210 	    uint64_t, past_ret);
211 
212 	if (ktriage_code != KDBG_TRIAGE_VM_SANITIZE_SKIP) {
213 		ktriage_record(thread_tid(current_thread()),
214 		    KDBG_TRIAGE_EVENTID(KDBG_TRIAGE_SUBSYS_VM_SANITIZE, KDBG_TRIAGE_RESERVED, ktriage_code),
215 		    past_ret /* arg */);
216 	}
217 
218 	return;
219 }
220