xref: /xnu-8792.41.9/osfmk/kern/backtrace.c (revision 5c2921b07a2480ab43ec66f5b9e41cb872bc554f)
1 // Copyright (c) 2016-2021 Apple Inc. All rights reserved.
2 //
3 // @APPLE_OSREFERENCE_LICENSE_HEADER_START@
4 //
5 // This file contains Original Code and/or Modifications of Original Code
6 // as defined in and that are subject to the Apple Public Source License
7 // Version 2.0 (the 'License'). You may not use this file except in
8 // compliance with the License. The rights granted to you under the License
9 // may not be used to create, or enable the creation or redistribution of,
10 // unlawful or unlicensed copies of an Apple operating system, or to
11 // circumvent, violate, or enable the circumvention or violation of, any
12 // terms of an Apple operating system software license agreement.
13 //
14 // Please obtain a copy of the License at
15 // http://www.opensource.apple.com/apsl/ and read it before using this file.
16 //
17 // The Original Code and all software distributed under the License are
18 // distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
19 // EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
20 // INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
22 // Please see the License for the specific language governing rights and
23 // limitations under the License.
24 //
25 // @APPLE_OSREFERENCE_LICENSE_HEADER_END@
26 
27 #include <stddef.h>
28 #include <stdint.h>
29 
30 #include <kern/assert.h>
31 #include <kern/backtrace.h>
32 #include <kern/cambria_layout.h>
33 #include <kern/thread.h>
34 #include <sys/errno.h>
35 #include <vm/vm_map.h>
36 
37 #if defined(__arm64__)
38 #include <arm/cpu_data.h>
39 #include <arm/cpu_data_internal.h>
40 #endif // defined(__arm64__)
41 
42 #if defined(HAS_APPLE_PAC)
43 #include <ptrauth.h>
44 #endif // defined(HAS_APPLE_PAC)
45 
46 #if XNU_MONITOR
47 #define IN_PPLSTK_BOUNDS(__addr) \
48    (((uintptr_t)(__addr) >= (uintptr_t)pmap_stacks_start) && \
49    ((uintptr_t)(__addr) < (uintptr_t)pmap_stacks_end))
50 #endif
51 
52 #if __x86_64__
53 static void
_backtrace_packed_out_of_reach(void)54 _backtrace_packed_out_of_reach(void)
55 {
56 	/*
57 	 * This symbol is used to replace frames that have been "JIT-ed"
58 	 * or dynamically inserted in the kernel by some kext in a regular
59 	 * VM mapping that might be outside of the filesets.
60 	 *
61 	 * This is an Intel only issue.
62 	 */
63 }
64 #endif
65 
66 // Pack an address according to a particular packing format.
67 static size_t
_backtrace_pack_addr(backtrace_pack_t packing,uint8_t * dst,size_t dst_size,uintptr_t addr)68 _backtrace_pack_addr(backtrace_pack_t packing, uint8_t *dst, size_t dst_size,
69     uintptr_t addr)
70 {
71 	switch (packing) {
72 	case BTP_NONE:
73 		if (dst_size >= sizeof(addr)) {
74 			memcpy(dst, &addr, sizeof(addr));
75 		}
76 		return sizeof(addr);
77 	case BTP_KERN_OFFSET_32:;
78 		uintptr_t addr_delta = addr - vm_kernel_stext;
79 		int32_t addr_packed = (int32_t)addr_delta;
80 #if __x86_64__
81 		if ((uintptr_t)(int32_t)addr_delta != addr_delta) {
82 			addr = (vm_offset_t)&_backtrace_packed_out_of_reach;
83 			addr_delta = addr - vm_kernel_stext;
84 			addr_packed = (int32_t)addr_delta;
85 		}
86 #else
87 		assert((uintptr_t)(int32_t)addr_delta == addr_delta);
88 #endif
89 		if (dst_size >= sizeof(addr_packed)) {
90 			memcpy(dst, &addr_packed, sizeof(addr_packed));
91 		}
92 		return sizeof(addr_packed);
93 	default:
94 		panic("backtrace: unknown packing format %d", packing);
95 	}
96 }
97 
98 // Since it's only called from threads that we're going to keep executing,
99 // if there's bad data the system is going to die eventually.  If this function
100 // is inlined, it doesn't record the frame of the function it's inside (because
101 // there's no stack frame), so prevent that.
102 static size_t __attribute__((noinline, not_tail_called))
backtrace_internal(backtrace_pack_t packing,uint8_t * bt,size_t btsize,void * start_frame,int64_t addr_offset,backtrace_info_t * info_out)103 backtrace_internal(backtrace_pack_t packing, uint8_t *bt,
104     size_t btsize, void *start_frame, int64_t addr_offset,
105     backtrace_info_t *info_out)
106 {
107 	thread_t thread = current_thread();
108 	uintptr_t *fp;
109 	size_t size_used = 0;
110 	uintptr_t top, bottom;
111 	bool in_valid_stack;
112 
113 	assert(bt != NULL);
114 	assert(btsize > 0);
115 
116 	fp = start_frame;
117 	bottom = thread->kernel_stack;
118 	top = bottom + kernel_stack_size;
119 
120 #define IN_STK_BOUNDS(__addr) \
121 	(((uintptr_t)(__addr) >= (uintptr_t)bottom) && \
122 	((uintptr_t)(__addr) < (uintptr_t)top))
123 
124 	in_valid_stack = IN_STK_BOUNDS(fp);
125 #if XNU_MONITOR
126 	in_valid_stack |= IN_PPLSTK_BOUNDS(fp);
127 #endif /* XNU_MONITOR */
128 
129 	if (!in_valid_stack) {
130 		fp = NULL;
131 	}
132 
133 	while (fp != NULL && size_used < btsize) {
134 		uintptr_t *next_fp = (uintptr_t *)*fp;
135 		// Return address is one word higher than frame pointer.
136 		uintptr_t ret_addr = *(fp + 1);
137 
138 		// If the frame pointer is 0, backtracing has reached the top of
139 		// the stack and there is no return address.  Some stacks might not
140 		// have set this up, so bounds check, as well.
141 		in_valid_stack = IN_STK_BOUNDS(next_fp);
142 #if XNU_MONITOR
143 		in_valid_stack |= IN_PPLSTK_BOUNDS(next_fp);
144 #endif /* XNU_MONITOR */
145 
146 		if (next_fp == NULL || !in_valid_stack) {
147 			break;
148 		}
149 
150 #if defined(HAS_APPLE_PAC)
151 		// Return addresses are signed by arm64e ABI, so strip it.
152 		uintptr_t pc = (uintptr_t)ptrauth_strip((void *)ret_addr,
153 		    ptrauth_key_return_address);
154 #else // defined(HAS_APPLE_PAC)
155 		uintptr_t pc = ret_addr;
156 #endif // !defined(HAS_APPLE_PAC)
157 		pc += addr_offset;
158 		size_used += _backtrace_pack_addr(packing, bt + size_used,
159 		    btsize - size_used, pc);
160 
161 		// Stacks grow down; backtracing should be moving to higher addresses.
162 		if (next_fp <= fp) {
163 #if XNU_MONITOR
164 			bool fp_in_pplstack = IN_PPLSTK_BOUNDS(fp);
165 			bool fp_in_kstack = IN_STK_BOUNDS(fp);
166 			bool next_fp_in_pplstack = IN_PPLSTK_BOUNDS(fp);
167 			bool next_fp_in_kstack = IN_STK_BOUNDS(fp);
168 
169 			// This check is verbose; it is basically checking whether this
170 			// thread is switching between the kernel stack and the CPU stack.
171 			// If so, ignore the fact that frame pointer has switched directions
172 			// (as it is a symptom of switching stacks).
173 			if (((fp_in_pplstack) && (next_fp_in_kstack)) ||
174 			    ((fp_in_kstack) && (next_fp_in_pplstack))) {
175 				break;
176 			}
177 #else /* XNU_MONITOR */
178 			break;
179 #endif /* !XNU_MONITOR */
180 		}
181 		fp = next_fp;
182 	}
183 
184 	if (info_out) {
185 		backtrace_info_t info = BTI_NONE;
186 #if __LP64__
187 		info |= BTI_64_BIT;
188 #endif
189 		if (fp != NULL && size_used >= btsize) {
190 			info |= BTI_TRUNCATED;
191 		}
192 		*info_out = info;
193 	}
194 
195 	return size_used;
196 #undef IN_STK_BOUNDS
197 }
198 
199 static kern_return_t
interrupted_kernel_pc_fp(uintptr_t * pc,uintptr_t * fp)200 interrupted_kernel_pc_fp(uintptr_t *pc, uintptr_t *fp)
201 {
202 #if defined(__x86_64__)
203 	x86_saved_state_t *state;
204 	bool state_64;
205 	uint64_t cs;
206 
207 	state = current_cpu_datap()->cpu_int_state;
208 	if (!state) {
209 		return KERN_FAILURE;
210 	}
211 
212 	state_64 = is_saved_state64(state);
213 
214 	if (state_64) {
215 		cs = saved_state64(state)->isf.cs;
216 	} else {
217 		cs = saved_state32(state)->cs;
218 	}
219 	// Return early if interrupted a thread in user space.
220 	if ((cs & SEL_PL) == SEL_PL_U) {
221 		return KERN_FAILURE;
222 	}
223 
224 	if (state_64) {
225 		*pc = saved_state64(state)->isf.rip;
226 		*fp = saved_state64(state)->rbp;
227 	} else {
228 		*pc = saved_state32(state)->eip;
229 		*fp = saved_state32(state)->ebp;
230 	}
231 
232 #elif defined(__arm64__)
233 
234 	struct arm_saved_state *state;
235 	bool state_64;
236 
237 	state = getCpuDatap()->cpu_int_state;
238 	if (!state) {
239 		return KERN_FAILURE;
240 	}
241 	state_64 = is_saved_state64(state);
242 
243 	// Return early if interrupted a thread in user space.
244 	if (PSR64_IS_USER(get_saved_state_cpsr(state))) {
245 		return KERN_FAILURE;
246 	}
247 
248 	*pc = get_saved_state_pc(state);
249 	*fp = get_saved_state_fp(state);
250 
251 #else // !defined(__arm64__) && !defined(__x86_64__)
252 #error "unsupported architecture"
253 #endif // !defined(__arm64__) && !defined(__x86_64__)
254 
255 	return KERN_SUCCESS;
256 }
257 
258 __attribute__((always_inline))
259 static uintptr_t
_backtrace_preamble(struct backtrace_control * ctl,uintptr_t * start_frame_out)260 _backtrace_preamble(struct backtrace_control *ctl, uintptr_t *start_frame_out)
261 {
262 	backtrace_flags_t flags = ctl ? ctl->btc_flags : 0;
263 	uintptr_t start_frame = ctl ? ctl->btc_frame_addr : 0;
264 	uintptr_t pc = 0;
265 	if (flags & BTF_KERN_INTERRUPTED) {
266 		assert(ml_at_interrupt_context() == TRUE);
267 
268 		uintptr_t fp;
269 		kern_return_t kr = interrupted_kernel_pc_fp(&pc, &fp);
270 		if (kr != KERN_SUCCESS) {
271 			return 0;
272 		}
273 		*start_frame_out = start_frame ?: fp;
274 	} else if (start_frame == 0) {
275 		*start_frame_out = (uintptr_t)__builtin_frame_address(0);
276 	} else {
277 		*start_frame_out = start_frame;
278 	}
279 	return pc;
280 }
281 
282 unsigned int __attribute__((noinline))
backtrace(uintptr_t * bt,unsigned int max_frames,struct backtrace_control * ctl,backtrace_info_t * info_out)283 backtrace(uintptr_t *bt, unsigned int max_frames,
284     struct backtrace_control *ctl, backtrace_info_t *info_out)
285 {
286 	unsigned int len_adj = 0;
287 	uintptr_t start_frame = ctl ? ctl->btc_frame_addr : 0;
288 	uintptr_t pc = _backtrace_preamble(ctl, &start_frame);
289 	if (pc) {
290 		bt[0] = pc;
291 		if (max_frames == 1) {
292 			return 1;
293 		}
294 		bt += 1;
295 		max_frames -= 1;
296 		len_adj += 1;
297 	}
298 
299 	size_t size = backtrace_internal(BTP_NONE, (uint8_t *)bt,
300 	    max_frames * sizeof(uintptr_t), (void *)start_frame,
301 	    ctl ? ctl->btc_addr_offset : 0, info_out);
302 	// NULL-terminate the list, if space is available.
303 	unsigned int len = size / sizeof(uintptr_t);
304 	if (len != max_frames) {
305 		bt[len] = 0;
306 	}
307 
308 	return len + len_adj;
309 }
310 
311 // Backtrace the current thread's kernel stack as a packed representation.
312 size_t
backtrace_packed(backtrace_pack_t packing,uint8_t * bt,size_t btsize,struct backtrace_control * ctl,backtrace_info_t * info_out)313 backtrace_packed(backtrace_pack_t packing, uint8_t *bt, size_t btsize,
314     struct backtrace_control *ctl,
315     backtrace_info_t *info_out)
316 {
317 	unsigned int size_adj = 0;
318 	uintptr_t start_frame = ctl ? ctl->btc_frame_addr : 0;
319 	uintptr_t pc = _backtrace_preamble(ctl, &start_frame);
320 	if (pc) {
321 		size_adj = _backtrace_pack_addr(packing, bt, btsize, pc);
322 		if (size_adj >= btsize) {
323 			return size_adj;
324 		}
325 		btsize -= size_adj;
326 	}
327 
328 	size_t written_size = backtrace_internal(packing, (uint8_t *)bt, btsize,
329 	    (void *)start_frame, ctl ? ctl->btc_addr_offset : 0, info_out);
330 	return written_size + size_adj;
331 }
332 
333 // Convert an array of addresses to a packed representation.
334 size_t
backtrace_pack(backtrace_pack_t packing,uint8_t * dst,size_t dst_size,const uintptr_t * src,unsigned int src_len)335 backtrace_pack(backtrace_pack_t packing, uint8_t *dst, size_t dst_size,
336     const uintptr_t *src, unsigned int src_len)
337 {
338 	size_t dst_offset = 0;
339 	for (unsigned int i = 0; i < src_len; i++) {
340 		size_t pack_size = _backtrace_pack_addr(packing, dst + dst_offset,
341 		    dst_size - dst_offset, src[i]);
342 		if (dst_offset + pack_size >= dst_size) {
343 			return dst_offset;
344 		}
345 		dst_offset += pack_size;
346 	}
347 	return dst_offset;
348 }
349 
350 // Convert a packed backtrace to an array of addresses.
351 unsigned int
backtrace_unpack(backtrace_pack_t packing,uintptr_t * dst,unsigned int dst_len,const uint8_t * src,size_t src_size)352 backtrace_unpack(backtrace_pack_t packing, uintptr_t *dst, unsigned int dst_len,
353     const uint8_t *src, size_t src_size)
354 {
355 	switch (packing) {
356 	case BTP_NONE:;
357 		size_t unpack_size = MIN(dst_len * sizeof(uintptr_t), src_size);
358 		memmove(dst, src, unpack_size);
359 		return (unsigned int)(unpack_size / sizeof(uintptr_t));
360 	case BTP_KERN_OFFSET_32:;
361 		unsigned int src_len = src_size / sizeof(int32_t);
362 		unsigned int unpack_len = MIN(src_len, dst_len);
363 		for (unsigned int i = 0; i < unpack_len; i++) {
364 			int32_t addr = 0;
365 			memcpy(&addr, src + i * sizeof(int32_t), sizeof(int32_t));
366 			dst[i] = vm_kernel_stext + (uintptr_t)addr;
367 		}
368 		return unpack_len;
369 	default:
370 		panic("backtrace: unknown packing format %d", packing);
371 	}
372 }
373 
374 static errno_t
_backtrace_copyin(void * __unused ctx,void * dst,user_addr_t src,size_t size)375 _backtrace_copyin(void * __unused ctx, void *dst, user_addr_t src, size_t size)
376 {
377 	return copyin((user_addr_t)src, dst, size);
378 }
379 
380 errno_t
backtrace_user_copy_error(void * ctx,void * dst,user_addr_t src,size_t size)381 backtrace_user_copy_error(void *ctx, void *dst, user_addr_t src, size_t size)
382 {
383 #pragma unused(ctx, dst, src, size)
384 	return EFAULT;
385 }
386 
387 unsigned int
backtrace_user(uintptr_t * bt,unsigned int max_frames,const struct backtrace_control * ctl_in,struct backtrace_user_info * info_out)388 backtrace_user(uintptr_t *bt, unsigned int max_frames,
389     const struct backtrace_control *ctl_in,
390     struct backtrace_user_info *info_out)
391 {
392 	static const struct backtrace_control ctl_default = {
393 		.btc_user_copy = _backtrace_copyin,
394 	};
395 	const struct backtrace_control *ctl = ctl_in ?: &ctl_default;
396 	uintptr_t pc = 0, next_fp = 0;
397 	uintptr_t fp = ctl->btc_frame_addr;
398 	bool custom_fp = fp != 0;
399 	int64_t addr_offset = ctl ? ctl->btc_addr_offset : 0;
400 	vm_map_t map = NULL, old_map = NULL;
401 	unsigned int frame_index = 0;
402 	int error = 0;
403 	size_t frame_size = 0;
404 	bool truncated = false;
405 	bool user_64 = false;
406 	bool allow_async = true;
407 	bool has_async = false;
408 	uintptr_t async_frame_addr = 0;
409 	unsigned int async_index = 0;
410 
411 	backtrace_user_copy_fn copy = ctl->btc_user_copy ?: _backtrace_copyin;
412 	bool custom_copy = copy != _backtrace_copyin;
413 	void *ctx = ctl->btc_user_copy_context;
414 
415 	void *thread = ctl->btc_user_thread;
416 	void *cur_thread = NULL;
417 	if (thread == NULL) {
418 		cur_thread = current_thread();
419 		thread = cur_thread;
420 	}
421 	task_t task = get_threadtask(thread);
422 
423 	assert(task != NULL);
424 	assert(bt != NULL);
425 	assert(max_frames > 0);
426 
427 	if (!custom_copy) {
428 		assert(ml_get_interrupts_enabled() == TRUE);
429 		if (!ml_get_interrupts_enabled()) {
430 			error = EDEADLK;
431 		}
432 
433 		if (cur_thread == NULL) {
434 			cur_thread = current_thread();
435 		}
436 		if (thread != cur_thread) {
437 			map = get_task_map_reference(task);
438 			if (map == NULL) {
439 				error = ENOMEM;
440 				goto out;
441 			}
442 			old_map = vm_map_switch(map);
443 		}
444 	}
445 
446 #define SWIFT_ASYNC_FP_BIT (0x1ULL << 60)
447 #define SWIFT_ASYNC_FP(FP) (((FP) & SWIFT_ASYNC_FP_BIT) != 0)
448 #define SWIFT_ASYNC_FP_CLEAR(FP) ((FP) & ~SWIFT_ASYNC_FP_BIT)
449 
450 #if defined(__x86_64__)
451 
452 	// Don't allow a malformed user stack to copy arbitrary kernel data.
453 #define INVALID_USER_FP(FP) ((FP) == 0 || !IS_USERADDR64_CANONICAL((FP)))
454 
455 	x86_saved_state_t *state = get_user_regs(thread);
456 	if (!state) {
457 		error = EINVAL;
458 		goto out;
459 	}
460 
461 	user_64 = is_saved_state64(state);
462 	if (user_64) {
463 		pc = saved_state64(state)->isf.rip;
464 		fp = fp != 0 ? fp : saved_state64(state)->rbp;
465 	} else {
466 		pc = saved_state32(state)->eip;
467 		fp = fp != 0 ? fp : saved_state32(state)->ebp;
468 	}
469 
470 #elif defined(__arm64__)
471 
472 	struct arm_saved_state *state = get_user_regs(thread);
473 	if (!state) {
474 		error = EINVAL;
475 		goto out;
476 	}
477 
478 	user_64 = is_saved_state64(state);
479 	pc = get_saved_state_pc(state);
480 	fp = fp != 0 ? fp : get_saved_state_fp(state);
481 
482 	// ARM expects stack frames to be aligned to 16 bytes.
483 #define INVALID_USER_FP(FP) (((FP) & 0x3UL) != 0UL)
484 
485 #else // defined(__arm64__) || defined(__x86_64__)
486 #error "unsupported architecture"
487 #endif // !defined(__arm64__) && !defined(__x86_64__)
488 
489 	// Only capture the save state PC without a custom frame pointer to walk.
490 	if (!ctl || ctl->btc_frame_addr == 0) {
491 		bt[frame_index++] = pc + addr_offset;
492 	}
493 
494 	if (frame_index >= max_frames) {
495 		goto out;
496 	}
497 
498 	if (fp == 0) {
499 		// If the FP is zeroed, then there's no stack to walk, by design.  This
500 		// happens for workq threads that are being sent back to user space or
501 		// during boot-strapping operations on other kinds of threads.
502 		goto out;
503 	} else if (INVALID_USER_FP(fp)) {
504 		// Still capture the PC in this case, but mark the stack as truncated
505 		// and "faulting."  (Using the frame pointer on a call stack would cause
506 		// an exception.)
507 		error = EFAULT;
508 		truncated = true;
509 		goto out;
510 	}
511 
512 	union {
513 		struct {
514 			uint64_t fp;
515 			uint64_t ret;
516 		} u64;
517 		struct {
518 			uint32_t fp;
519 			uint32_t ret;
520 		} u32;
521 	} frame;
522 
523 	frame_size = 2 * (user_64 ? 8 : 4);
524 
525 	while (fp != 0 && frame_index < max_frames) {
526 		error = copy(ctx, (char *)&frame, fp, frame_size);
527 		if (error) {
528 			truncated = true;
529 			goto out;
530 		}
531 
532 		// Capture this return address before tripping over any errors finding
533 		// the next frame to follow.
534 		uintptr_t ret_addr = user_64 ? frame.u64.ret : frame.u32.ret;
535 #if defined(HAS_APPLE_PAC)
536 		// Return addresses are signed by arm64e ABI, so strip off the auth
537 		// bits.
538 		bt[frame_index++] = (uintptr_t)ptrauth_strip((void *)ret_addr,
539 		    ptrauth_key_return_address) + addr_offset;
540 #else // defined(HAS_APPLE_PAC)
541 		bt[frame_index++] = ret_addr + addr_offset;
542 #endif // !defined(HAS_APPLE_PAC)
543 
544 		// Find the next frame to follow.
545 		next_fp = user_64 ? frame.u64.fp : frame.u32.fp;
546 		bool async_frame = allow_async && SWIFT_ASYNC_FP(next_fp);
547 		// There is no 32-bit ABI for Swift async call stacks.
548 		if (user_64 && async_frame) {
549 			async_index = frame_index - 1;
550 			// The async context pointer is just below the stack frame.
551 			user_addr_t async_ctx_ptr = fp - 8;
552 			user_addr_t async_ctx = 0;
553 			error = copy(ctx, (char *)&async_ctx, async_ctx_ptr,
554 			    sizeof(async_ctx));
555 			if (error) {
556 				goto out;
557 			}
558 #if defined(HAS_APPLE_PAC)
559 			async_frame_addr = (uintptr_t)ptrauth_strip((void *)async_ctx,
560 			    ptrauth_key_process_dependent_data);
561 #else // defined(HAS_APPLE_PAC)
562 			async_frame_addr = (uintptr_t)async_ctx;
563 #endif // !defined(HAS_APPLE_PAC)
564 			has_async = true;
565 			allow_async = false;
566 		}
567 		next_fp = SWIFT_ASYNC_FP_CLEAR(next_fp);
568 #if defined(HAS_APPLE_PAC)
569 		next_fp = (uintptr_t)ptrauth_strip((void *)next_fp,
570 		    ptrauth_key_process_dependent_data);
571 #endif // defined(HAS_APPLE_PAC)
572 		if (INVALID_USER_FP(next_fp)) {
573 			break;
574 		}
575 
576 		// Stacks grow down; backtracing should be moving to higher addresses,
577 		// unless a custom frame pointer is provided, in which case, an async
578 		// stack might be walked, which is allocated on the heap in any order.
579 		if ((next_fp == fp) || (!custom_fp && next_fp < fp)) {
580 			break;
581 		}
582 		fp = next_fp;
583 	}
584 
585 out:
586 	if (old_map != NULL) {
587 		(void)vm_map_switch(old_map);
588 		vm_map_deallocate(map);
589 	}
590 
591 	// NULL-terminate the list, if space is available.
592 	if (frame_index < max_frames) {
593 		bt[frame_index] = 0;
594 	}
595 
596 	if (info_out) {
597 		info_out->btui_error = error;
598 		backtrace_info_t info = user_64 ? BTI_64_BIT : BTI_NONE;
599 		bool out_of_space = !INVALID_USER_FP(fp) && frame_index == max_frames;
600 		if (truncated || out_of_space) {
601 			info |= BTI_TRUNCATED;
602 		}
603 		if (out_of_space && error == 0) {
604 			info_out->btui_next_frame_addr = fp;
605 		}
606 		info_out->btui_info = info;
607 		info_out->btui_async_start_index = async_index;
608 		info_out->btui_async_frame_addr = async_frame_addr;
609 	}
610 
611 	return frame_index;
612 }
613