| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <limits.h> |
| #include <pthread.h> |
| #include <stdint.h> |
| #include <threads.h> |
| #include <unistd.h> |
| #include <zircon/syscalls.h> |
| |
| #include <runtime/tls.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| // We request one-page stacks, so collisions are easy to catch. |
| uintptr_t PageOf(const void* ptr) { |
| return reinterpret_cast<uintptr_t>(ptr) & -static_cast<uintptr_t>(zx_system_get_page_size()); |
| } |
| |
| struct StackTestInfo { |
| bool is_pthread; |
| char** environ; |
| const void* safe_stack; |
| const char* unsafe_stack; |
| const char* tls_buf; |
| const void* tp; |
| const void* unsafe_start; |
| const void* unsafe_ptr; |
| const void* unsafe_end; |
| const void* scs_ptr; |
| }; |
| |
| void* DoStackTest(void* arg) { |
| StackTestInfo* info = reinterpret_cast<StackTestInfo*>(arg); |
| |
| info->safe_stack = __builtin_frame_address(0); |
| |
| // The compiler sees this pointer escape, so it should know |
| // that this belongs on the unsafe stack. |
| char unsafe_stack[64]; |
| zx_system_get_version(unsafe_stack, sizeof(unsafe_stack)); |
| |
| // Likewise, the tls_buf is used. |
| static thread_local char tls_buf[64]; |
| zx_system_get_version(tls_buf, sizeof(tls_buf)); |
| |
| info->tp = zxr_tp_get(); |
| |
| info->environ = environ; |
| info->unsafe_stack = unsafe_stack; |
| info->tls_buf = tls_buf; |
| |
| #if __has_feature(safe_stack) |
| info->unsafe_start = __builtin___get_unsafe_stack_start(); |
| info->unsafe_ptr = __builtin___get_unsafe_stack_ptr(); |
| info->unsafe_end = __builtin___get_unsafe_stack_top(); |
| #endif |
| |
| #if __has_feature(shadow_call_stack) |
| #ifdef __aarch64__ |
| __asm__("mov %0, x18" : "=r"(info->scs_ptr)); |
| #else |
| #error "what shadow-call-stack ABI??" |
| #endif |
| #endif |
| |
| return nullptr; |
| } |
| |
| void CheckThreadStackInfo(StackTestInfo* info) { |
| EXPECT_NOT_NULL(info->environ, "environ unset"); |
| EXPECT_NOT_NULL(info->safe_stack, "CFA is null"); |
| EXPECT_NOT_NULL(info->unsafe_stack, "local's taken address is null"); |
| EXPECT_NOT_NULL(info->tls_buf, "thread_local's taken address is null"); |
| |
| if (__has_feature(safe_stack) || info->is_pthread) { |
| EXPECT_NE(PageOf(info->safe_stack), PageOf(info->environ), "safe stack collides with environ"); |
| } |
| |
| // The environ array sits on the main thread's unsafe stack. But we can't |
| // verify that it does since it might not be on the same page. So just check |
| // on the pThread |
| if (info->is_pthread) { |
| EXPECT_NE(PageOf(info->unsafe_stack), PageOf(info->environ), |
| "unsafe stack collides with environ"); |
| } |
| |
| EXPECT_NE(PageOf(info->tls_buf), PageOf(info->environ), "TLS collides with environ"); |
| |
| EXPECT_NE(PageOf(info->tls_buf), PageOf(info->safe_stack), "TLS collides with safe stack"); |
| |
| EXPECT_NE(PageOf(info->tls_buf), PageOf(info->unsafe_stack), "TLS collides with unsafe stack"); |
| |
| EXPECT_NE(PageOf(info->tp), PageOf(info->environ), "thread pointer collides with environ"); |
| |
| EXPECT_NE(PageOf(info->tp), PageOf(info->safe_stack), "thread pointer collides with safe stack"); |
| |
| EXPECT_NE(PageOf(info->tp), PageOf(info->unsafe_stack), |
| "thread pointer collides with unsafe stack"); |
| |
| #if __has_feature(safe_stack) |
| if (info->is_pthread) { |
| EXPECT_EQ(PageOf(info->unsafe_start), PageOf(info->unsafe_ptr), |
| "reported unsafe start and ptr not nearby"); |
| } |
| |
| EXPECT_LE(info->unsafe_start, info->unsafe_ptr, "unsafe ptr is out of bounds"); |
| |
| EXPECT_LE(info->unsafe_ptr, info->unsafe_end, "unsafe ptr is out of bounds"); |
| |
| EXPECT_EQ(PageOf(info->unsafe_stack), PageOf(info->unsafe_ptr), |
| "unsafe stack and reported ptr not nearby"); |
| |
| EXPECT_NE(PageOf(info->unsafe_stack), PageOf(info->safe_stack), |
| "unsafe stack collides with safe stack"); |
| #endif |
| |
| #if __has_feature(shadow_call_stack) |
| EXPECT_NOT_NULL(info->scs_ptr, "shadow call stack pointer not set"); |
| |
| EXPECT_NE(PageOf(info->scs_ptr), PageOf(info->environ), |
| "shadow call stack collides with environ"); |
| |
| EXPECT_NE(PageOf(info->scs_ptr), PageOf(info->tls_buf), "shadow call stack collides with TLS"); |
| |
| EXPECT_NE(PageOf(info->scs_ptr), PageOf(info->safe_stack), |
| "shadow call stack collides with safe stack"); |
| |
| EXPECT_NE(PageOf(info->scs_ptr), PageOf(info->unsafe_stack), |
| "shadow call stack collides with unsafe stack"); |
| |
| EXPECT_NE(PageOf(info->scs_ptr), PageOf(info->tp), |
| "shadow call stack collides with thread pointer"); |
| #elif defined(__clang__) && defined(__aarch64__) |
| #error "This test should always be built with -fsanitize=shadow-call-stack" |
| #endif |
| } |
| |
| // This instance of the test is lossy, because it's possible |
| // one of our single stacks spans multiple pages. We can't |
| // get the main thread's stack down to a single page because |
| // the unittest machinery needs more than that. |
| TEST(StackTest, MainThreadStack) { |
| StackTestInfo info; |
| info.is_pthread = false; |
| |
| DoStackTest(&info); |
| |
| CheckThreadStackInfo(&info); |
| } |
| |
| // Spawn a thread with a one-page stack. |
| TEST(StackTest, ThreadStack) { |
| EXPECT_LE(PTHREAD_STACK_MIN, zx_system_get_page_size()); |
| |
| pthread_attr_t attr; |
| ASSERT_EQ(0, pthread_attr_init(&attr)); |
| ASSERT_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)); |
| pthread_t thread; |
| |
| StackTestInfo info; |
| info.is_pthread = true; |
| ASSERT_EQ(0, pthread_create(&thread, &attr, &DoStackTest, &info)); |
| ASSERT_EQ(0, pthread_join(thread, nullptr)); |
| CheckThreadStackInfo(&info); |
| } |
| |
| } // namespace |