// Copyright (c) 2016-2020 Apple Computer, Inc. All rights reserved. #include #include #include #include #include #include #include #include #include #include T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); enum test_scenario { USER_SCENARIO = 0, RESUME_SCENARIO = 1, }; enum kernel_test_scenario { PACK_UNPACK_SCENARIO = 0, PACKED_SCENARIO = 1, }; #define USER_FRAMES (12) #define MAX_SYSCALL_SETUP_FRAMES (3) #define NON_RECURSE_FRAMES (2) #define ASYNC_FRAMES (2 + NON_RECURSE_FRAMES) static const char *user_bt[USER_FRAMES] = { "backtrace_thread", "recurse_a", "recurse_b", "recurse_a", "recurse_b", "recurse_a", "recurse_b", "recurse_a", "recurse_b", "recurse_a", "recurse_b", "expect_callstack", }; struct callstack_exp { bool in_syscall_setup; unsigned int syscall_frames; const char **callstack; size_t callstack_len; unsigned int nchecked; }; #if __has_feature(ptrauth_calls) #define __ptrauth_swift_async_context_parent \ __ptrauth(ptrauth_key_process_independent_data, 1, 0xbda2) #define __ptrauth_swift_async_context_resume \ __ptrauth(ptrauth_key_function_pointer, 1, 0xd707) #else #define __ptrauth_swift_async_context_parent #define __ptrauth_swift_async_context_resume #endif // This struct fakes the Swift AsyncContext struct which is used by // the Swift concurrency runtime. We only care about the first 2 fields. struct fake_async_context { struct fake_async_context* __ptrauth_swift_async_context_parent next; void(*__ptrauth_swift_async_context_resume resume_pc)(void); }; static void level1_func() { } static void level2_func() { } // Create a chain of fake async contexts static alignas(16) struct fake_async_context level1 = { 0, level1_func }; static alignas(16) struct fake_async_context level2 = { &level1, level2_func }; static const char *async_bt[ASYNC_FRAMES] = { "level1_func", "level2_func", "backtrace_thread_async", "expect_async_callstack", }; static void expect_frame(struct callstack_exp *cs, CSSymbolRef symbol, unsigned long addr, unsigned int bt_idx) { if (CSIsNull(symbol)) { if (!cs->in_syscall_setup) { T_FAIL("invalid symbol for address %#lx at frame %d", addr, bt_idx); } return; } const char *name = CSSymbolGetName(symbol); if (name) { if (cs->in_syscall_setup) { if (strcmp(name, cs->callstack[cs->callstack_len - 1]) == 0) { cs->in_syscall_setup = false; cs->syscall_frames = bt_idx; T_LOG("found start of controlled stack at frame %u, expected " "index %zu", cs->syscall_frames, cs->callstack_len - 1); } else { T_LOG("found syscall setup symbol %s at frame %u", name, bt_idx); } } if (!cs->in_syscall_setup) { if (cs->nchecked >= cs->callstack_len) { T_LOG("frame %2u: skipping system frame %s", bt_idx, name); } else { size_t frame_idx = cs->callstack_len - cs->nchecked - 1; T_EXPECT_EQ_STR(name, cs->callstack[frame_idx], "frame %2zu: saw '%s', expected '%s'", frame_idx, name, cs->callstack[frame_idx]); } cs->nchecked++; } } else { if (!cs->in_syscall_setup) { T_ASSERT_NOTNULL(name, NULL, "symbol should not be NULL"); } } } static bool is_kernel_64_bit(void) { static dispatch_once_t k64_once; static bool k64 = false; dispatch_once(&k64_once, ^{ int errb; int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 /* kernproc */ }; struct kinfo_proc kp; size_t len = sizeof(kp); errb = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &kp, &len, NULL, 0); T_QUIET; T_ASSERT_POSIX_SUCCESS(errb, "sysctl({ CTL_KERN, KERN_PROC, KERN_PROC_PID, 0})"); k64 = kp.kp_proc.p_flag & P_LP64; T_LOG("executing with a %s-bit kernel", k64 ? "64" : "32"); }); return k64; } // Use an extra, non-inlineable function so that any frames after expect_stack // can be safely ignored. This insulates the test from changes in how syscalls // are called by Libc and the kernel. static void __attribute__((noinline, not_tail_called)) backtrace_current_thread_wrapper(enum test_scenario scenario, uint64_t *bt, size_t *bt_filled) { int ret = sysctlbyname("kern.backtrace.user", bt, bt_filled, NULL, scenario); getpid(); // Really prevent tail calls. if (ret == -1 && errno == ENOENT) { T_SKIP("release kernel: kern.backtrace.user sysctl returned ENOENT"); } T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname(\"kern.backtrace.user\")"); T_LOG("kernel returned %zu frame backtrace", *bt_filled); } static CSSymbolicatorRef get_symbolicator(void) { static CSSymbolicatorRef user_symb; static dispatch_once_t expect_stack_once; dispatch_once(&expect_stack_once, ^{ user_symb = CSSymbolicatorCreateWithTask(mach_task_self()); T_QUIET; T_ASSERT_FALSE(CSIsNull(user_symb), NULL); T_QUIET; T_ASSERT_TRUE(CSSymbolicatorIsTaskValid(user_symb), NULL); }); return user_symb; } static void __attribute__((noinline, not_tail_called)) expect_callstack(enum test_scenario scenario) { uint64_t bt[USER_FRAMES + MAX_SYSCALL_SETUP_FRAMES] = { 0 }; CSSymbolicatorRef user_symb = get_symbolicator(); size_t bt_filled = USER_FRAMES + MAX_SYSCALL_SETUP_FRAMES; backtrace_current_thread_wrapper(scenario, bt, &bt_filled); unsigned int bt_len = (unsigned int)bt_filled; T_EXPECT_GE(bt_len, (unsigned int)USER_FRAMES, "at least %u frames should be present in backtrace", USER_FRAMES); T_EXPECT_LE(bt_len, (unsigned int)USER_FRAMES + MAX_SYSCALL_SETUP_FRAMES, "at most %u frames should be present in backtrace", USER_FRAMES + MAX_SYSCALL_SETUP_FRAMES); struct callstack_exp callstack = { .in_syscall_setup = true, .syscall_frames = 0, .callstack = user_bt, .callstack_len = USER_FRAMES, .nchecked = 0, }; for (unsigned int i = 0; i < bt_len; i++) { uintptr_t addr; #if !defined(__LP64__) // Backtrace frames come out as kernel words; convert them back to user // uintptr_t for 32-bit processes. if (is_kernel_64_bit()) { addr = (uintptr_t)(bt[i]); } else { addr = (uintptr_t)(((uint32_t *)bt)[i]); } #else // defined(__LP32__) addr = (uintptr_t)bt[i]; #endif // defined(__LP32__) CSSymbolRef symbol = CSSymbolicatorGetSymbolWithAddressAtTime( user_symb, addr, kCSNow); expect_frame(&callstack, symbol, addr, i); } T_EXPECT_GE(callstack.nchecked, USER_FRAMES, "checked enough frames for correct symbols"); } static int __attribute__((noinline, not_tail_called)) recurse_a(enum test_scenario, unsigned int frames); static int __attribute__((noinline, not_tail_called)) recurse_b(enum test_scenario, unsigned int frames); static int __attribute__((noinline, not_tail_called)) recurse_a(enum test_scenario scenario, unsigned int frames) { if (frames == 1) { expect_callstack(scenario); getpid(); // Really prevent tail calls. return 0; } return recurse_b(scenario, frames - 1) + 1; } static int __attribute__((noinline, not_tail_called)) recurse_b(enum test_scenario scenario, unsigned int frames) { if (frames == 1) { expect_callstack(scenario); getpid(); // Really prevent tail calls. return 0; } return recurse_a(scenario, frames - 1) + 1; } static void __attribute__((noinline, not_tail_called)) expect_async_callstack(void) { uint64_t bt[ASYNC_FRAMES + MAX_SYSCALL_SETUP_FRAMES] = { 0 }; CSSymbolicatorRef user_symb = get_symbolicator(); size_t bt_filled = ASYNC_FRAMES + MAX_SYSCALL_SETUP_FRAMES; backtrace_current_thread_wrapper(USER_SCENARIO, bt, &bt_filled); unsigned int bt_len = (unsigned int)bt_filled; T_EXPECT_GE(bt_len, (unsigned int)ASYNC_FRAMES, "at least %u frames should be present in backtrace", ASYNC_FRAMES); T_EXPECT_LE(bt_len, (unsigned int)ASYNC_FRAMES + MAX_SYSCALL_SETUP_FRAMES, "at most %u frames should be present in backtrace", ASYNC_FRAMES + MAX_SYSCALL_SETUP_FRAMES); struct callstack_exp callstack = { .in_syscall_setup = true, .syscall_frames = 0, .callstack = async_bt, .callstack_len = ASYNC_FRAMES, .nchecked = 0, }; for (unsigned int i = 0; i < bt_len; i++) { uintptr_t addr; #if !defined(__LP64__) // Backtrace frames come out as kernel words; convert them back to user // uintptr_t for 32-bit processes. if (is_kernel_64_bit()) { addr = (uintptr_t)(bt[i]); } else { addr = (uintptr_t)(((uint32_t *)bt)[i]); } #else // defined(__LP32__) addr = (uintptr_t)bt[i]; #endif // defined(__LP32__) CSSymbolRef symbol = CSSymbolicatorGetSymbolWithAddressAtTime( user_symb, addr, kCSNow); expect_frame(&callstack, symbol, addr, i); } T_EXPECT_GE(callstack.nchecked, ASYNC_FRAMES, "checked enough frames for correct symbols"); } static void * backtrace_thread_async(void * __unused arg) { uint64_t *fp = __builtin_frame_address(0); // We cannot use a variable of pointer type, because this ABI is valid // on arm64_32 where pointers are 32bits, but the context pointer will // still be stored in a 64bits slot on the stack. #if __has_feature(ptrauth_calls) #define __stack_context_auth __ptrauth(ptrauth_key_process_dependent_data, 1, \ 0xc31a) struct fake_async_context * __stack_context_auth ctx = &level2; #else // __has_feature(ptrauth_calls) /* struct fake_async_context * */uint64_t ctx = (uintptr_t)&level2; #endif // !__has_feature(ptrauth_calls) // The signature of an async frame on the OS stack is: // [ , , ] // The Async context must be right before the saved FP on the stack. This // should happen naturally in an optimized build as it is the only // variable on the stack. // This function cannot use T_ASSERT_* becuse it changes the stack // layout. assert((uintptr_t)fp - (uintptr_t)&ctx == 8); // Modify the saved FP on the stack to include the async frame marker *fp |= (0x1ULL << 60); expect_async_callstack(); return NULL; } static void * backtrace_thread(void *arg) { unsigned int calls; enum test_scenario scenario = (enum test_scenario)arg; // backtrace_thread, recurse_a, recurse_b, ..., __sysctlbyname // // Always make one less call for this frame (backtrace_thread). calls = USER_FRAMES - NON_RECURSE_FRAMES; T_LOG("backtrace thread calling into %d frames (already at %d frames)", calls, NON_RECURSE_FRAMES); (void)recurse_a(scenario, calls); return NULL; } T_DECL(backtrace_user, "test that the kernel can backtrace user stacks", T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(true), T_META_TAG_VM_PREFERRED) { pthread_t thread; // Run the test from a different thread to insulate it from libdarwintest // setup. T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, backtrace_thread, (void *)USER_SCENARIO), "create additional thread to backtrace"); T_QUIET; T_ASSERT_POSIX_ZERO(pthread_join(thread, NULL), NULL); } T_DECL(backtrace_user_bounds, "test that the kernel doesn't write frames out of expected bounds", T_META_TAG_VM_PREFERRED) { uint64_t bt_init[USER_FRAMES] = {}; size_t bt_filled = USER_FRAMES, bt_filled_after = 0; int error = 0; kern_return_t kr = KERN_FAILURE; void *bt_page = NULL; void *guard_page = NULL; void *bt_start = NULL; // The backtrace addresses come back as kernel words. size_t kword_size = is_kernel_64_bit() ? 8 : 4; // Get an idea of how many frames to expect. int ret = sysctlbyname("kern.backtrace.user", bt_init, &bt_filled, NULL, 0); if (ret == -1 && errno == ENOENT) { T_SKIP("release kernel: kern.backtrace.user missing"); } T_ASSERT_POSIX_SUCCESS(error, "sysctlbyname(\"kern.backtrace.user\")"); // Allocate two pages -- a first one that's valid and a second that // will be non-writeable to catch a copyout that's too large. bt_page = mmap(NULL, vm_page_size * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); T_WITH_ERRNO; T_ASSERT_NE(bt_page, MAP_FAILED, "allocated backtrace pages"); guard_page = (char *)bt_page + vm_page_size; error = mprotect(guard_page, vm_page_size, PROT_READ); T_ASSERT_POSIX_SUCCESS(error, "mprotect(..., PROT_READ) guard page"); // Ensure the pages are set up as expected. kr = vm_write(mach_task_self(), (vm_address_t)bt_page, (vm_offset_t)&(int){ 12345 }, sizeof(int)); T_ASSERT_MACH_SUCCESS(kr, "should succeed in writing to backtrace page"); kr = vm_write(mach_task_self(), (vm_address_t)guard_page, (vm_offset_t)&(int){ 12345 }, sizeof(int)); T_ASSERT_NE(kr, KERN_SUCCESS, "should fail to write to guard page"); // Ask the kernel to write the backtrace just before the guard page. bt_start = (char *)guard_page - (kword_size * bt_filled); bt_filled_after = bt_filled; error = sysctlbyname("kern.backtrace.user", bt_start, &bt_filled_after, NULL, 0); T_EXPECT_POSIX_SUCCESS(error, "sysctlbyname(\"kern.backtrace.user\") just before guard page"); T_EXPECT_EQ(bt_filled, bt_filled_after, "both calls to backtrace should have filled in the same number of " "frames"); // Expect the kernel to fault when writing too far. bt_start = (char *)bt_start + 1; bt_filled_after = bt_filled; error = sysctlbyname("kern.backtrace.user", bt_start, &bt_filled_after, (void *)USER_SCENARIO, 0); T_EXPECT_POSIX_FAILURE(error, EFAULT, "sysctlbyname(\"kern.backtrace.user\") should fault one byte into " "guard page"); } T_DECL(backtrace_user_async, "test that the kernel can backtrace user async stacks", T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(false), T_META_TAG_VM_PREFERRED) { #if !defined(__LP64__) T_SKIP("unsupported on LP32"); #else // __LP32__ pthread_t thread; // Run the test from a different thread to insulate it from libdarwintest // setup. T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, backtrace_thread_async, NULL), "create additional thread to backtrace"); T_QUIET; T_ASSERT_POSIX_ZERO(pthread_join(thread, NULL), NULL); #endif // !__LP32__ } T_DECL(backtrace_user_resume, "test that the kernel can resume a backtrace into a smaller buffer", T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(false), T_META_TAG_VM_PREFERRED) { pthread_t thread; T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, backtrace_thread, (void *)RESUME_SCENARIO), "create additional thread to backtrace"); T_QUIET; T_ASSERT_POSIX_ZERO(pthread_join(thread, NULL), NULL); } T_DECL(backtrace_kernel_pack_unpack, "test that a kernel backtrace can be packed and unpacked losslessly", T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(false), T_META_TAG_VM_PREFERRED) { int error = sysctlbyname("kern.backtrace.kernel_tests", NULL, NULL, (void *)PACK_UNPACK_SCENARIO, 0); T_EXPECT_POSIX_SUCCESS(error, "sysctlbyname(\"kern.backtrace.kernel_tests\", PACK_UNPACK)"); } T_DECL(backtrace_kernel_packed, "test that a kernel backtrace can be recorded as packed losslessly", T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(false), T_META_TAG_VM_PREFERRED) { int error = sysctlbyname("kern.backtrace.kernel_tests", NULL, NULL, (void *)PACKED_SCENARIO, 0); T_EXPECT_POSIX_SUCCESS(error, "sysctlbyname(\"kern.backtrace.kernel_tests\", PACKED)"); } #pragma mark - utilities static void __attribute__((noinline, not_tail_called)) spin_forever(void) { while (true) { ; } } static void check_stack(uintptr_t fp, uintptr_t ctx) { if ((fp - ctx) != 0x8) { fprintf(stderr, "stack frame is not set up properly: " "%#lx, %#lx is %lx bytes away\n", fp, ctx, fp - ctx); exit(1); } } static void __attribute__((noinline, not_tail_called)) spin_backtrace_async(void) { uint64_t *fp = __builtin_frame_address(0); #if __has_feature(ptrauth_calls) struct fake_async_context * __stack_context_auth ctx = &level2; #else // __has_feature(ptrauth_calls) /* struct fake_async_context * */uint64_t ctx = (uintptr_t)&level2; #endif // !__has_feature(ptrauth_calls) check_stack((uintptr_t)fp, (uintptr_t)&ctx); *fp |= (0x1ULL << 60); spin_forever(); } T_DECL(backtrace_user_async_spin_forever, "try spinning forever with an async call stack set up", T_META_ENABLED(false), T_META_CHECK_LEAKS(false), T_META_ALL_VALID_ARCHS(false), T_META_TAG_VM_PREFERRED) { #if !defined(__LP64__) T_SKIP("unsupported on LP32"); #else // __LP32__ spin_backtrace_async(); #endif // !__LP32__ }