]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kern/backtrace.c
xnu-6153.41.3.tar.gz
[apple/xnu.git] / osfmk / kern / backtrace.c
index b47ce7940b41e79f4a5d365c3abc4c19d9ba9028..59667c828222f44c7f6a472a00d8dce8849a4817 100644 (file)
 #include <arm/cpu_data_internal.h>
 #endif
 
+#if defined(HAS_APPLE_PAC)
+#include <ptrauth.h>
+#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