]>
Commit | Line | Data |
---|---|---|
39037602 A |
1 | /* |
2 | * Copyright (c) 2016 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 <stddef.h> | |
30 | #include <stdint.h> | |
31 | ||
32 | #include <kern/assert.h> | |
33 | #include <kern/backtrace.h> | |
34 | #include <kern/thread.h> | |
35 | #include <sys/errno.h> | |
36 | #include <vm/vm_map.h> | |
37 | ||
5ba3f43e A |
38 | #if defined(__arm__) || defined(__arm64__) |
39 | #include <arm/cpu_data.h> | |
40 | #include <arm/cpu_data_internal.h> | |
41 | #endif | |
42 | ||
cb323159 A |
43 | #if defined(HAS_APPLE_PAC) |
44 | #include <ptrauth.h> | |
45 | #endif | |
39037602 | 46 | |
d9a64523 | 47 | |
cb323159 A |
48 | unsigned int __attribute__((noinline)) |
49 | backtrace(uintptr_t *bt, unsigned int max_frames, bool *was_truncated_out) | |
39037602 | 50 | { |
cb323159 A |
51 | return backtrace_frame(bt, max_frames, __builtin_frame_address(0), |
52 | was_truncated_out); | |
39037602 A |
53 | } |
54 | ||
55 | /* | |
56 | * This function captures a backtrace from the current stack and returns the | |
57 | * number of frames captured, limited by max_frames and starting at start_frame. | |
58 | * It's fast because it does no checking to make sure there isn't bad data. | |
59 | * Since it's only called from threads that we're going to keep executing, | |
60 | * if there's bad data we were going to die eventually. If this function is | |
61 | * inlined, it doesn't record the frame of the function it's inside (because | |
62 | * there's no stack frame). | |
63 | */ | |
cb323159 A |
64 | unsigned int __attribute__((noinline, not_tail_called)) |
65 | backtrace_frame(uintptr_t *bt, unsigned int max_frames, void *start_frame, | |
66 | bool *was_truncated_out) | |
39037602 A |
67 | { |
68 | thread_t thread = current_thread(); | |
69 | uintptr_t *fp; | |
cb323159 | 70 | unsigned int frame_index = 0; |
39037602 | 71 | uintptr_t top, bottom; |
5ba3f43e | 72 | bool in_valid_stack; |
39037602 A |
73 | |
74 | assert(bt != NULL); | |
75 | assert(max_frames > 0); | |
76 | ||
77 | fp = start_frame; | |
78 | bottom = thread->kernel_stack; | |
79 | top = bottom + kernel_stack_size; | |
80 | ||
5ba3f43e A |
81 | #define IN_STK_BOUNDS(__addr) \ |
82 | (((uintptr_t)(__addr) >= (uintptr_t)bottom) && \ | |
83 | ((uintptr_t)(__addr) < (uintptr_t)top)) | |
84 | ||
85 | in_valid_stack = IN_STK_BOUNDS(fp); | |
86 | ||
87 | if (!in_valid_stack) { | |
39037602 A |
88 | fp = NULL; |
89 | } | |
90 | ||
91 | while (fp != NULL && frame_index < max_frames) { | |
5ba3f43e | 92 | uintptr_t *next_fp = (uintptr_t *)*fp; |
d9a64523 | 93 | uintptr_t ret_addr = *(fp + 1); /* return address is one word higher than frame pointer */ |
39037602 A |
94 | |
95 | /* | |
96 | * If the frame pointer is 0, backtracing has reached the top of | |
97 | * the stack and there is no return address. Some stacks might not | |
98 | * have set this up, so bounds check, as well. | |
99 | */ | |
5ba3f43e A |
100 | in_valid_stack = IN_STK_BOUNDS(next_fp); |
101 | ||
0a7de745 | 102 | if (next_fp == NULL || !in_valid_stack) { |
39037602 A |
103 | break; |
104 | } | |
105 | ||
cb323159 A |
106 | #if defined(HAS_APPLE_PAC) |
107 | /* return addresses signed by arm64e ABI */ | |
108 | bt[frame_index++] = (uintptr_t) ptrauth_strip((void *)ret_addr, ptrauth_key_return_address); | |
109 | #else /* defined(HAS_APPLE_PAC) */ | |
d9a64523 | 110 | bt[frame_index++] = ret_addr; |
cb323159 | 111 | #endif /* !defined(HAS_APPLE_PAC) */ |
39037602 A |
112 | |
113 | /* stacks grow down; backtracing should be moving to higher addresses */ | |
114 | if (next_fp <= fp) { | |
115 | break; | |
116 | } | |
117 | fp = next_fp; | |
118 | } | |
119 | ||
cb323159 A |
120 | /* NULL-terminate the list, if space is available */ |
121 | if (frame_index != max_frames) { | |
122 | bt[frame_index] = 0; | |
123 | } | |
124 | ||
125 | if (fp != NULL && frame_index == max_frames && was_truncated_out) { | |
126 | *was_truncated_out = true; | |
127 | } | |
128 | ||
39037602 | 129 | return frame_index; |
5ba3f43e | 130 | #undef IN_STK_BOUNDS |
39037602 A |
131 | } |
132 | ||
133 | #if defined(__x86_64__) | |
134 | ||
135 | static kern_return_t | |
136 | interrupted_kernel_pc_fp(uintptr_t *pc, uintptr_t *fp) | |
137 | { | |
138 | x86_saved_state_t *state; | |
139 | bool state_64; | |
140 | uint64_t cs; | |
141 | ||
142 | state = current_cpu_datap()->cpu_int_state; | |
143 | if (!state) { | |
144 | return KERN_FAILURE; | |
145 | } | |
146 | ||
147 | state_64 = is_saved_state64(state); | |
148 | ||
149 | if (state_64) { | |
150 | cs = saved_state64(state)->isf.cs; | |
151 | } else { | |
152 | cs = saved_state32(state)->cs; | |
153 | } | |
154 | /* return early if interrupted a thread in user space */ | |
155 | if ((cs & SEL_PL) == SEL_PL_U) { | |
156 | return KERN_FAILURE; | |
157 | } | |
158 | ||
159 | if (state_64) { | |
160 | *pc = saved_state64(state)->isf.rip; | |
161 | *fp = saved_state64(state)->rbp; | |
162 | } else { | |
163 | *pc = saved_state32(state)->eip; | |
164 | *fp = saved_state32(state)->ebp; | |
165 | } | |
166 | return KERN_SUCCESS; | |
167 | } | |
168 | ||
5ba3f43e A |
169 | #elif defined(__arm64__) |
170 | ||
171 | static kern_return_t | |
172 | interrupted_kernel_pc_fp(uintptr_t *pc, uintptr_t *fp) | |
173 | { | |
174 | struct arm_saved_state *state; | |
175 | bool state_64; | |
176 | ||
177 | state = getCpuDatap()->cpu_int_state; | |
178 | if (!state) { | |
179 | return KERN_FAILURE; | |
180 | } | |
181 | state_64 = is_saved_state64(state); | |
182 | ||
183 | /* return early if interrupted a thread in user space */ | |
184 | if (PSR64_IS_USER(get_saved_state_cpsr(state))) { | |
185 | return KERN_FAILURE; | |
186 | } | |
187 | ||
188 | *pc = get_saved_state_pc(state); | |
189 | *fp = get_saved_state_fp(state); | |
190 | return KERN_SUCCESS; | |
191 | } | |
192 | ||
193 | #elif defined(__arm__) | |
194 | ||
195 | static kern_return_t | |
196 | interrupted_kernel_pc_fp(uintptr_t *pc, uintptr_t *fp) | |
197 | { | |
198 | struct arm_saved_state *state; | |
199 | ||
200 | state = getCpuDatap()->cpu_int_state; | |
201 | if (!state) { | |
202 | return KERN_FAILURE; | |
203 | } | |
204 | ||
205 | /* return early if interrupted a thread in user space */ | |
206 | if (PSR_IS_USER(get_saved_state_cpsr(state))) { | |
207 | return KERN_FAILURE; | |
208 | } | |
209 | ||
210 | *pc = get_saved_state_pc(state); | |
211 | *fp = get_saved_state_fp(state); | |
212 | return KERN_SUCCESS; | |
213 | } | |
214 | ||
39037602 A |
215 | #else /* defined(__arm__) */ |
216 | #error "interrupted_kernel_pc_fp: unsupported architecture" | |
217 | #endif /* !defined(__arm__) */ | |
218 | ||
cb323159 A |
219 | unsigned int |
220 | backtrace_interrupted(uintptr_t *bt, unsigned int max_frames, | |
221 | bool *was_truncated_out) | |
39037602 A |
222 | { |
223 | uintptr_t pc; | |
5ba3f43e | 224 | uintptr_t fp; |
39037602 A |
225 | kern_return_t kr; |
226 | ||
227 | assert(bt != NULL); | |
228 | assert(max_frames > 0); | |
229 | assert(ml_at_interrupt_context() == TRUE); | |
230 | ||
5ba3f43e | 231 | kr = interrupted_kernel_pc_fp(&pc, &fp); |
39037602 A |
232 | if (kr != KERN_SUCCESS) { |
233 | return 0; | |
234 | } | |
235 | ||
236 | bt[0] = pc; | |
237 | if (max_frames == 1) { | |
238 | return 1; | |
239 | } | |
240 | ||
cb323159 A |
241 | return backtrace_frame(bt + 1, max_frames - 1, (void *)fp, |
242 | was_truncated_out) + 1; | |
39037602 A |
243 | } |
244 | ||
ea3f0419 | 245 | unsigned int |
cb323159 | 246 | backtrace_user(uintptr_t *bt, unsigned int max_frames, |
ea3f0419 | 247 | int *error_out, bool *user_64_out, bool *was_truncated_out) |
39037602 | 248 | { |
cb323159 | 249 | return backtrace_thread_user(current_thread(), bt, max_frames, |
ea3f0419 | 250 | error_out, user_64_out, was_truncated_out); |
39037602 A |
251 | } |
252 | ||
ea3f0419 | 253 | unsigned int |
cb323159 | 254 | backtrace_thread_user(void *thread, uintptr_t *bt, unsigned int max_frames, |
ea3f0419 | 255 | int *error_out, bool *user_64_out, bool *was_truncated_out) |
39037602 A |
256 | { |
257 | bool user_64; | |
cb323159 | 258 | uintptr_t pc = 0, fp = 0, next_fp = 0; |
d9a64523 | 259 | vm_map_t map = NULL, old_map = NULL; |
cb323159 | 260 | unsigned int frame_index = 0; |
39037602 | 261 | int err = 0; |
cb323159 | 262 | size_t frame_size = 0; |
39037602 | 263 | |
39037602 A |
264 | assert(bt != NULL); |
265 | assert(max_frames > 0); | |
39037602 A |
266 | |
267 | #if defined(__x86_64__) | |
268 | ||
269 | /* don't allow a malformed user stack to copyin arbitrary kernel data */ | |
270 | #define INVALID_USER_FP(FP) ((FP) == 0 || !IS_USERADDR64_CANONICAL((FP))) | |
271 | ||
272 | x86_saved_state_t *state = get_user_regs(thread); | |
273 | ||
274 | if (!state) { | |
275 | return EINVAL; | |
276 | } | |
277 | ||
278 | user_64 = is_saved_state64(state); | |
279 | if (user_64) { | |
280 | pc = saved_state64(state)->isf.rip; | |
281 | fp = saved_state64(state)->rbp; | |
282 | } else { | |
283 | pc = saved_state32(state)->eip; | |
284 | fp = saved_state32(state)->ebp; | |
285 | } | |
286 | ||
5ba3f43e A |
287 | #elif defined(__arm64__) |
288 | ||
289 | /* ARM expects stack frames to be aligned to 16 bytes */ | |
290 | #define INVALID_USER_FP(FP) ((FP) == 0 || ((FP) & 0x3UL) != 0UL) | |
291 | ||
292 | struct arm_saved_state *state = get_user_regs(thread); | |
293 | if (!state) { | |
294 | return EINVAL; | |
295 | } | |
296 | ||
297 | user_64 = is_saved_state64(state); | |
298 | pc = get_saved_state_pc(state); | |
299 | fp = get_saved_state_fp(state); | |
300 | ||
301 | #elif defined(__arm__) | |
302 | ||
303 | /* ARM expects stack frames to be aligned to 16 bytes */ | |
304 | #define INVALID_USER_FP(FP) ((FP) == 0 || ((FP) & 0x3UL) != 0UL) | |
305 | ||
306 | struct arm_saved_state *state = get_user_regs(thread); | |
307 | if (!state) { | |
308 | return EINVAL; | |
309 | } | |
310 | ||
311 | user_64 = false; | |
312 | pc = get_saved_state_pc(state); | |
313 | fp = get_saved_state_fp(state); | |
314 | ||
39037602 A |
315 | #else /* defined(__arm__) */ |
316 | #error "backtrace_thread_user: unsupported architecture" | |
317 | #endif /* !defined(__arm__) */ | |
318 | ||
d9a64523 A |
319 | bt[frame_index++] = pc; |
320 | ||
321 | if (frame_index >= max_frames) { | |
322 | goto out; | |
323 | } | |
324 | ||
325 | if (INVALID_USER_FP(fp)) { | |
326 | goto out; | |
327 | } | |
328 | ||
329 | assert(ml_get_interrupts_enabled() == TRUE); | |
330 | if (!ml_get_interrupts_enabled()) { | |
94ff46dc | 331 | goto out; |
39037602 A |
332 | } |
333 | ||
334 | union { | |
335 | struct { | |
336 | uint64_t fp; | |
337 | uint64_t ret; | |
338 | } u64; | |
339 | struct { | |
340 | uint32_t fp; | |
341 | uint32_t ret; | |
342 | } u32; | |
343 | } frame; | |
39037602 | 344 | |
cb323159 | 345 | frame_size = 2 * (user_64 ? 8 : 4); |
39037602 | 346 | |
d9a64523 A |
347 | /* switch to the correct map, for copyin */ |
348 | if (thread != current_thread()) { | |
349 | map = get_task_map_reference(get_threadtask(thread)); | |
350 | if (map == NULL) { | |
94ff46dc | 351 | goto out; |
d9a64523 A |
352 | } |
353 | old_map = vm_map_switch(map); | |
354 | } else { | |
355 | map = NULL; | |
39037602 A |
356 | } |
357 | ||
358 | while (fp != 0 && frame_index < max_frames) { | |
359 | err = copyin(fp, (char *)&frame, frame_size); | |
360 | if (err) { | |
cb323159 A |
361 | if (was_truncated_out) { |
362 | *was_truncated_out = true; | |
363 | } | |
39037602 A |
364 | goto out; |
365 | } | |
366 | ||
367 | next_fp = user_64 ? frame.u64.fp : frame.u32.fp; | |
368 | ||
369 | if (INVALID_USER_FP(next_fp)) { | |
370 | break; | |
371 | } | |
372 | ||
d9a64523 | 373 | uintptr_t ret_addr = user_64 ? frame.u64.ret : frame.u32.ret; |
cb323159 A |
374 | #if defined(HAS_APPLE_PAC) |
375 | /* return addresses signed by arm64e ABI */ | |
376 | bt[frame_index++] = (uintptr_t)ptrauth_strip((void *)ret_addr, | |
377 | ptrauth_key_return_address); | |
378 | #else /* defined(HAS_APPLE_PAC) */ | |
d9a64523 | 379 | bt[frame_index++] = ret_addr; |
cb323159 | 380 | #endif /* !defined(HAS_APPLE_PAC) */ |
39037602 A |
381 | |
382 | /* stacks grow down; backtracing should be moving to higher addresses */ | |
383 | if (next_fp <= fp) { | |
384 | break; | |
385 | } | |
386 | fp = next_fp; | |
387 | } | |
388 | ||
389 | out: | |
390 | if (map) { | |
391 | (void)vm_map_switch(old_map); | |
392 | vm_map_deallocate(map); | |
393 | } | |
394 | ||
cb323159 A |
395 | /* NULL-terminate the list, if space is available */ |
396 | if (frame_index != max_frames) { | |
397 | bt[frame_index] = 0; | |
398 | } | |
399 | ||
400 | if (fp != 0 && frame_index == max_frames && was_truncated_out) { | |
401 | *was_truncated_out = true; | |
402 | } | |
403 | ||
404 | if (user_64_out) { | |
405 | *user_64_out = user_64; | |
406 | } | |
ea3f0419 A |
407 | if (error_out) { |
408 | *error_out = err; | |
409 | } | |
cb323159 | 410 | |
ea3f0419 | 411 | return frame_index; |
39037602 A |
412 | #undef INVALID_USER_FP |
413 | } |