| // 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 <zircon/syscalls.h> |
| #include <pthread.h> |
| #include <runtime/tls.h> |
| #include <stdint.h> |
| #include <threads.h> |
| #include <unistd.h> |
| #include <unittest/unittest.h> |
| |
| // We request one-page stacks, so collisions are easy to catch. |
| static uintptr_t page_of(const void* ptr) { |
| return (uintptr_t)ptr & -PAGE_SIZE; |
| } |
| |
| static bool do_stack_tests(bool is_main_thread) { |
| BEGIN_TEST; |
| |
| const void* 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]; |
| (void)zx_system_get_version(unsafe_stack, sizeof(unsafe_stack)); |
| |
| // Likewise, the tls_buf is used. |
| static thread_local char tls_buf[64]; |
| (void)zx_system_get_version(tls_buf, sizeof(tls_buf)); |
| |
| const void* tp = zxr_tp_get(); |
| |
| EXPECT_NONNULL(environ, "environ unset"); |
| EXPECT_NONNULL(safe_stack, "CFA is null"); |
| EXPECT_NONNULL(unsafe_stack, "local's taken address is null"); |
| EXPECT_NONNULL(tls_buf, "thread_local's taken address is null"); |
| EXPECT_NONNULL(tp, "thread pointer is null"); |
| |
| if (__has_feature(safe_stack) || !is_main_thread) { |
| EXPECT_NE(page_of(safe_stack), page_of(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. |
| if (!is_main_thread) { |
| EXPECT_NE(page_of(unsafe_stack), page_of(environ), |
| "unsafe stack collides with environ"); |
| } |
| |
| EXPECT_NE(page_of(tls_buf), page_of(environ), |
| "TLS collides with environ"); |
| |
| EXPECT_NE(page_of(tls_buf), page_of(safe_stack), |
| "TLS collides with safe stack"); |
| |
| EXPECT_NE(page_of(tls_buf), page_of(unsafe_stack), |
| "TLS collides with unsafe stack"); |
| |
| EXPECT_NE(page_of(tp), page_of(environ), |
| "thread pointer collides with environ"); |
| |
| EXPECT_NE(page_of(tp), page_of(safe_stack), |
| "thread pointer collides with safe stack"); |
| |
| EXPECT_NE(page_of(tp), page_of(unsafe_stack), |
| "thread pointer collides with unsafe stack"); |
| |
| #ifdef __clang__ |
| # if __has_feature(safe_stack) |
| const void* unsafe_start = __builtin___get_unsafe_stack_start(); |
| const void* unsafe_ptr = __builtin___get_unsafe_stack_ptr(); |
| |
| if (!is_main_thread) { |
| EXPECT_EQ(page_of(unsafe_start), page_of(unsafe_ptr), |
| "reported unsafe start and ptr not nearby"); |
| } |
| |
| EXPECT_EQ(page_of(unsafe_stack), page_of(unsafe_ptr), |
| "unsafe stack and reported ptr not nearby"); |
| |
| EXPECT_NE(page_of(unsafe_stack), page_of(safe_stack), |
| "unsafe stack collides with safe stack"); |
| # endif |
| #endif |
| |
| END_TEST; |
| } |
| |
| // 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. |
| static bool main_thread_stack_tests(void) { |
| return do_stack_tests(true); |
| } |
| |
| static void* thread_stack_tests(void* arg) { |
| return (void*)(uintptr_t)do_stack_tests(false); |
| } |
| |
| // Spawn a thread with a one-page stack. |
| static bool other_thread_stack_tests(void) { |
| BEGIN_TEST; |
| |
| EXPECT_LE(PTHREAD_STACK_MIN, 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; |
| ASSERT_EQ(0, pthread_create(&thread, &attr, &thread_stack_tests, 0), ""); |
| void* result; |
| ASSERT_EQ(0, pthread_join(thread, &result), ""); |
| bool other_thread_ok = (uintptr_t)result; |
| EXPECT_TRUE(other_thread_ok, ""); |
| |
| END_TEST; |
| } |
| |
| BEGIN_TEST_CASE(stack_tests) |
| RUN_TEST(main_thread_stack_tests) |
| RUN_TEST(other_thread_stack_tests) |
| END_TEST_CASE(stack_tests) |