X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/5ba3f43ea354af8ad55bea84372a2bc834d8757c..4ba76501152d51ccb5647018f3192c6096367d48:/osfmk/kern/backtrace.c diff --git a/osfmk/kern/backtrace.c b/osfmk/kern/backtrace.c index b47ce7940..59667c828 100644 --- a/osfmk/kern/backtrace.c +++ b/osfmk/kern/backtrace.c @@ -40,11 +40,16 @@ #include #endif +#if defined(HAS_APPLE_PAC) +#include +#endif + -uint32_t __attribute__((noinline)) -backtrace(uintptr_t *bt, uint32_t max_frames) +unsigned int __attribute__((noinline)) +backtrace(uintptr_t *bt, unsigned int max_frames, bool *was_truncated_out) { - return backtrace_frame(bt, max_frames, __builtin_frame_address(0)); + return backtrace_frame(bt, max_frames, __builtin_frame_address(0), + was_truncated_out); } /* @@ -56,12 +61,13 @@ backtrace(uintptr_t *bt, uint32_t max_frames) * inlined, it doesn't record the frame of the function it's inside (because * there's no stack frame). */ -uint32_t __attribute__((noinline,not_tail_called)) -backtrace_frame(uintptr_t *bt, uint32_t max_frames, void *start_frame) +unsigned int __attribute__((noinline, not_tail_called)) +backtrace_frame(uintptr_t *bt, unsigned int max_frames, void *start_frame, + bool *was_truncated_out) { thread_t thread = current_thread(); uintptr_t *fp; - uint32_t frame_index = 0; + unsigned int frame_index = 0; uintptr_t top, bottom; bool in_valid_stack; @@ -84,6 +90,7 @@ backtrace_frame(uintptr_t *bt, uint32_t max_frames, void *start_frame) while (fp != NULL && frame_index < max_frames) { uintptr_t *next_fp = (uintptr_t *)*fp; + uintptr_t ret_addr = *(fp + 1); /* return address is one word higher than frame pointer */ /* * If the frame pointer is 0, backtracing has reached the top of @@ -92,13 +99,16 @@ backtrace_frame(uintptr_t *bt, uint32_t max_frames, void *start_frame) */ in_valid_stack = IN_STK_BOUNDS(next_fp); - if (next_fp == NULL || !in_valid_stack) - { + if (next_fp == NULL || !in_valid_stack) { break; } - /* return address is one word higher than frame pointer */ - bt[frame_index++] = *(fp + 1); +#if defined(HAS_APPLE_PAC) + /* return addresses signed by arm64e ABI */ + bt[frame_index++] = (uintptr_t) ptrauth_strip((void *)ret_addr, ptrauth_key_return_address); +#else /* defined(HAS_APPLE_PAC) */ + bt[frame_index++] = ret_addr; +#endif /* !defined(HAS_APPLE_PAC) */ /* stacks grow down; backtracing should be moving to higher addresses */ if (next_fp <= fp) { @@ -107,6 +117,15 @@ backtrace_frame(uintptr_t *bt, uint32_t max_frames, void *start_frame) fp = next_fp; } + /* NULL-terminate the list, if space is available */ + if (frame_index != max_frames) { + bt[frame_index] = 0; + } + + if (fp != NULL && frame_index == max_frames && was_truncated_out) { + *was_truncated_out = true; + } + return frame_index; #undef IN_STK_BOUNDS } @@ -197,8 +216,9 @@ interrupted_kernel_pc_fp(uintptr_t *pc, uintptr_t *fp) #error "interrupted_kernel_pc_fp: unsupported architecture" #endif /* !defined(__arm__) */ -uint32_t -backtrace_interrupted(uintptr_t *bt, uint32_t max_frames) +unsigned int +backtrace_interrupted(uintptr_t *bt, unsigned int max_frames, + bool *was_truncated_out) { uintptr_t pc; uintptr_t fp; @@ -218,37 +238,32 @@ backtrace_interrupted(uintptr_t *bt, uint32_t max_frames) return 1; } - return backtrace_frame(bt + 1, max_frames - 1, (void *)fp); + return backtrace_frame(bt + 1, max_frames - 1, (void *)fp, + was_truncated_out) + 1; } int -backtrace_user(uintptr_t *bt, uint32_t max_frames, uint32_t *frames_out, - bool *user_64_out) +backtrace_user(uintptr_t *bt, unsigned int max_frames, + unsigned int *frames_out, bool *user_64_out, bool *was_truncated_out) { - return backtrace_thread_user(current_thread(), bt, max_frames, frames_out, - user_64_out); + return backtrace_thread_user(current_thread(), bt, max_frames, + frames_out, user_64_out, was_truncated_out); } int -backtrace_thread_user(void *thread, uintptr_t *bt, uint32_t max_frames, - uint32_t *frames_out, bool *user_64_out) +backtrace_thread_user(void *thread, uintptr_t *bt, unsigned int max_frames, + unsigned int *frames_out, bool *user_64_out, bool *was_truncated_out) { bool user_64; - uintptr_t pc, fp, next_fp; - vm_map_t map, old_map; - uint32_t frame_index = 0; + uintptr_t pc = 0, fp = 0, next_fp = 0; + vm_map_t map = NULL, old_map = NULL; + unsigned int frame_index = 0; int err = 0; - size_t frame_size; - - assert(ml_get_interrupts_enabled() == TRUE); - if (!ml_get_interrupts_enabled()) { - return EINVAL; - } + size_t frame_size = 0; assert(bt != NULL); assert(max_frames > 0); assert(frames_out != NULL); - assert(user_64_out != NULL); #if defined(__x86_64__) @@ -302,15 +317,19 @@ backtrace_thread_user(void *thread, uintptr_t *bt, uint32_t max_frames, #error "backtrace_thread_user: unsupported architecture" #endif /* !defined(__arm__) */ - /* switch to the correct map, for copyin */ - if (thread != current_thread()) { - map = get_task_map_reference(get_threadtask(thread)); - if (map == NULL) { - return EINVAL; - } - old_map = vm_map_switch(map); - } else { - map = NULL; + bt[frame_index++] = pc; + + if (frame_index >= max_frames) { + goto out; + } + + if (INVALID_USER_FP(fp)) { + goto out; + } + + assert(ml_get_interrupts_enabled() == TRUE); + if (!ml_get_interrupts_enabled()) { + goto out; } union { @@ -323,17 +342,26 @@ backtrace_thread_user(void *thread, uintptr_t *bt, uint32_t max_frames, uint32_t ret; } u32; } frame; - frame_size = 2 * (user_64 ? sizeof(uint64_t) : sizeof(uint32_t)); - bt[frame_index++] = pc; + frame_size = 2 * (user_64 ? 8 : 4); - if (INVALID_USER_FP(fp)) { - goto out; + /* switch to the correct map, for copyin */ + if (thread != current_thread()) { + map = get_task_map_reference(get_threadtask(thread)); + if (map == NULL) { + goto out; + } + old_map = vm_map_switch(map); + } else { + map = NULL; } while (fp != 0 && frame_index < max_frames) { err = copyin(fp, (char *)&frame, frame_size); if (err) { + if (was_truncated_out) { + *was_truncated_out = true; + } goto out; } @@ -343,7 +371,14 @@ backtrace_thread_user(void *thread, uintptr_t *bt, uint32_t max_frames, break; } - bt[frame_index++] = user_64 ? frame.u64.ret : frame.u32.ret; + uintptr_t ret_addr = user_64 ? frame.u64.ret : frame.u32.ret; +#if defined(HAS_APPLE_PAC) + /* return addresses signed by arm64e ABI */ + bt[frame_index++] = (uintptr_t)ptrauth_strip((void *)ret_addr, + ptrauth_key_return_address); +#else /* defined(HAS_APPLE_PAC) */ + bt[frame_index++] = ret_addr; +#endif /* !defined(HAS_APPLE_PAC) */ /* stacks grow down; backtracing should be moving to higher addresses */ if (next_fp <= fp) { @@ -358,7 +393,19 @@ out: vm_map_deallocate(map); } - *user_64_out = user_64; + /* NULL-terminate the list, if space is available */ + if (frame_index != max_frames) { + bt[frame_index] = 0; + } + + if (fp != 0 && frame_index == max_frames && was_truncated_out) { + *was_truncated_out = true; + } + + if (user_64_out) { + *user_64_out = user_64; + } + *frames_out = frame_index; return err; #undef INVALID_USER_FP