xref: /xnu-8020.140.41/bsd/kern/kern_backtrace.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
1 /*
2  * Copyright (c) 2016-2020 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 #include <kern/backtrace.h>
30 #include <kern/kalloc.h>
31 #include <sys/errno.h>
32 #include <sys/sysctl.h>
33 #include <sys/systm.h>
34 
35 #if DEVELOPMENT || DEBUG
36 
37 #define MAX_BACKTRACE  (128)
38 
39 #define BACKTRACE_USER (0)
40 #define BACKTRACE_USER_RESUME (1)
41 static int backtrace_user_sysctl SYSCTL_HANDLER_ARGS;
42 
43 #define BACKTRACE_KERN_TEST_PACK_UNPACK (0)
44 #define BACKTRACE_KERN_TEST_PACKED (1)
45 static int backtrace_kernel_sysctl SYSCTL_HANDLER_ARGS;
46 
47 SYSCTL_NODE(_kern, OID_AUTO, backtrace, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
48     "backtrace");
49 
50 SYSCTL_PROC(_kern_backtrace, OID_AUTO, user,
51     CTLFLAG_RW | CTLFLAG_LOCKED, (void *)BACKTRACE_USER,
52     sizeof(uint64_t), backtrace_user_sysctl, "O",
53     "take user backtrace of current thread");
54 
55 SYSCTL_PROC(_kern_backtrace, OID_AUTO, kernel_tests,
56     CTLFLAG_RW | CTLFLAG_LOCKED, (void *)BACKTRACE_USER,
57     sizeof(uint64_t), backtrace_kernel_sysctl, "O",
58     "take user backtrace of current thread");
59 
60 static int
61 backtrace_kernel_sysctl SYSCTL_HANDLER_ARGS
62 {
63 	unsigned int scenario = (unsigned int)req->newlen;
64 	uintptr_t *bt = NULL;
65 	uint8_t *packed_bt = NULL;
66 	uintptr_t *unpacked_bt = NULL;
67 	unsigned int bt_len = 0;
68 	size_t bt_size = 0;
69 	errno_t error = 0;
70 
71 	bt_len = 24;
72 	bt_size = sizeof(bt[0]) * bt_len;
73 	bt = kalloc_data(bt_size, Z_WAITOK | Z_ZERO);
74 	packed_bt = kalloc_data(bt_size, Z_WAITOK | Z_ZERO);
75 	unpacked_bt = kalloc_data(bt_size, Z_WAITOK | Z_ZERO);
76 	if (!bt || !packed_bt || !unpacked_bt) {
77 		error = ENOBUFS;
78 		goto out;
79 	}
80 	backtrace_info_t info = BTI_NONE;
81 	unsigned int len = backtrace(bt, bt_len, NULL, &info);
82 	backtrace_info_t packed_info = BTI_NONE;
83 	size_t packed_size = 0;
84 	if (scenario == BACKTRACE_KERN_TEST_PACK_UNPACK) {
85 		packed_size = backtrace_pack(BTP_KERN_OFFSET_32, packed_bt, bt_size,
86 		    bt, len);
87 	} else {
88 		packed_size = backtrace_packed(BTP_KERN_OFFSET_32, packed_bt, bt_size,
89 		    NULL, &packed_info);
90 	}
91 	unsigned int unpacked_len = backtrace_unpack(BTP_KERN_OFFSET_32,
92 	    unpacked_bt, bt_len, packed_bt, packed_size);
93 	if (unpacked_len != len) {
94 		printf("backtrace_tests: length %u != %u unpacked\n", len,
95 		    unpacked_len);
96 		error = ERANGE;
97 		goto out;
98 	}
99 	for (unsigned int i = 0; i < len; i++) {
100 		if (unpacked_bt[i] != bt[i]) {
101 			printf("backtrace_tests: bad address %u: 0x%lx != 0x%lx unpacked",
102 			    i, bt[i], unpacked_bt[i]);
103 			error = EINVAL;
104 		}
105 	}
106 
107 out:
108 	if (bt) {
109 		kfree_data(bt, bt_size);
110 	}
111 	if (packed_bt) {
112 		kfree_data(packed_bt, bt_size);
113 	}
114 	if (unpacked_bt) {
115 		kfree_data(unpacked_bt, bt_size);
116 	}
117 	return error;
118 }
119 
120 static int
121 backtrace_user_sysctl SYSCTL_HANDLER_ARGS
122 {
123 #pragma unused(oidp, arg1, arg2)
124 	unsigned int scenario = (unsigned int)req->newlen;
125 	uintptr_t *bt = NULL;
126 	unsigned int bt_len = 0, bt_filled = 0, bt_space = 0;
127 	size_t bt_size = 0;
128 	errno_t error = 0;
129 
130 	bool user_scenario = scenario == BACKTRACE_USER;
131 	bool resume_scenario = scenario == BACKTRACE_USER_RESUME;
132 	if (!user_scenario && !resume_scenario) {
133 		return ENOTSUP;
134 	}
135 
136 	if (req->oldptr == USER_ADDR_NULL || req->oldlen == 0) {
137 		return EFAULT;
138 	}
139 
140 	bt_len = req->oldlen > MAX_BACKTRACE ? MAX_BACKTRACE :
141 	    (unsigned int)req->oldlen;
142 	bt_size = sizeof(bt[0]) * bt_len;
143 	bt = kalloc_data(bt_size, Z_WAITOK | Z_ZERO);
144 	if (!bt) {
145 		return ENOBUFS;
146 	}
147 	bt_space = resume_scenario ? bt_len / 2 : bt_len;
148 	struct backtrace_user_info btinfo = BTUINFO_INIT;
149 	bt_filled = backtrace_user(bt, bt_space, NULL, &btinfo);
150 	error = btinfo.btui_error;
151 	if (error != 0) {
152 		goto out;
153 	}
154 	if (resume_scenario) {
155 		if (!(btinfo.btui_info & BTI_TRUNCATED)) {
156 			error = ENOSPC;
157 			goto out;
158 		}
159 		struct backtrace_control ctl = {
160 			.btc_frame_addr = btinfo.btui_next_frame_addr,
161 		};
162 		btinfo = BTUINFO_INIT;
163 		unsigned int bt_more = backtrace_user(bt + bt_filled, bt_space, &ctl,
164 		    &btinfo);
165 		error = btinfo.btui_error;
166 		if (error != 0) {
167 			goto out;
168 		}
169 		bt_filled += bt_more;
170 	}
171 	bt_filled = min(bt_filled, bt_len);
172 	if (btinfo.btui_async_frame_addr != 0 &&
173 	    btinfo.btui_async_start_index != 0) {
174 		// Put the async call stack inline after the real call stack.
175 		unsigned int start_index = btinfo.btui_async_start_index;
176 		uintptr_t frame_addr = btinfo.btui_async_frame_addr;
177 		unsigned int bt_left = bt_len - start_index;
178 		struct backtrace_control ctl = { .btc_frame_addr = frame_addr, };
179 		btinfo = BTUINFO_INIT;
180 		unsigned int async_filled = backtrace_user(bt + start_index, bt_left,
181 		    &ctl, &btinfo);
182 		error = btinfo.btui_error;
183 		if (error != 0) {
184 			goto out;
185 		}
186 		bt_filled = min(start_index + async_filled, bt_len);
187 	}
188 
189 	error = copyout(bt, req->oldptr, sizeof(bt[0]) * bt_filled);
190 	if (error) {
191 		goto out;
192 	}
193 	req->oldidx = bt_filled;
194 
195 out:
196 	kfree_data(bt, bt_size);
197 	return error;
198 }
199 
200 #endif /* DEVELOPMENT || DEBUG */
201