1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits.h>
6#include <zircon/syscalls.h>
7#include <pthread.h>
8#include <runtime/tls.h>
9#include <stdint.h>
10#include <threads.h>
11#include <unistd.h>
12#include <unittest/unittest.h>
13
14// We request one-page stacks, so collisions are easy to catch.
15static uintptr_t page_of(const void* ptr) {
16    return (uintptr_t)ptr & -PAGE_SIZE;
17}
18
19static bool do_stack_tests(bool one_page_stack) {
20    BEGIN_TEST;
21
22    const void* safe_stack = __builtin_frame_address(0);
23
24    // The compiler sees this pointer escape, so it should know
25    // that this belongs on the unsafe stack.
26    char unsafe_stack[64];
27    (void)zx_system_get_version(unsafe_stack, sizeof(unsafe_stack));
28
29    // Likewise, the tls_buf is used.
30    static thread_local char tls_buf[64];
31    (void)zx_system_get_version(tls_buf, sizeof(tls_buf));
32
33    const void* tp = zxr_tp_get();
34
35    EXPECT_NONNULL(environ, "environ unset");
36    EXPECT_NONNULL(safe_stack, "CFA is null");
37    EXPECT_NONNULL(unsafe_stack, "local's taken address is null");
38    EXPECT_NONNULL(tls_buf, "thread_local's taken address is null");
39    EXPECT_NONNULL(tp, "thread pointer is null");
40
41    EXPECT_NE(page_of(safe_stack), page_of(environ),
42              "safe stack collides with environ");
43
44    EXPECT_NE(page_of(unsafe_stack), page_of(environ),
45              "unsafe stack collides with environ");
46
47    EXPECT_NE(page_of(tls_buf), page_of(environ),
48              "TLS collides with environ");
49
50    EXPECT_NE(page_of(tls_buf), page_of(safe_stack),
51              "TLS collides with safe stack");
52
53    EXPECT_NE(page_of(tls_buf), page_of(unsafe_stack),
54              "TLS collides with unsafe stack");
55
56    EXPECT_NE(page_of(tp), page_of(environ),
57              "thread pointer collides with environ");
58
59    EXPECT_NE(page_of(tp), page_of(safe_stack),
60              "thread pointer collides with safe stack");
61
62    EXPECT_NE(page_of(tp), page_of(unsafe_stack),
63              "thread pointer collides with unsafe stack");
64
65#ifdef __clang__
66# if __has_feature(safe_stack)
67    const void* unsafe_start = __builtin___get_unsafe_stack_start();
68    const void* unsafe_ptr = __builtin___get_unsafe_stack_ptr();
69
70    if (one_page_stack) {
71        EXPECT_EQ(page_of(unsafe_start), page_of(unsafe_ptr),
72                  "reported unsafe start and ptr not nearby");
73    }
74
75    EXPECT_EQ(page_of(unsafe_stack), page_of(unsafe_ptr),
76              "unsafe stack and reported ptr not nearby");
77
78    EXPECT_NE(page_of(unsafe_stack), page_of(safe_stack),
79              "unsafe stack collides with safe stack");
80# endif
81#endif
82
83    END_TEST;
84}
85
86// This instance of the test is lossy, because it's possible
87// one of our single stacks spans multiple pages.  We can't
88// get the main thread's stack down to a single page because
89// the unittest machinery needs more than that.
90static bool main_thread_stack_tests(void) {
91    return do_stack_tests(false);
92}
93
94static void* thread_stack_tests(void* arg) {
95    return (void*)(uintptr_t)do_stack_tests(true);
96}
97
98// Spawn a thread with a one-page stack.
99static bool other_thread_stack_tests(void) {
100    BEGIN_TEST;
101
102    EXPECT_LE(PTHREAD_STACK_MIN, PAGE_SIZE, "");
103
104    pthread_attr_t attr;
105    ASSERT_EQ(0, pthread_attr_init(&attr), "");
106    ASSERT_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN), "");
107    pthread_t thread;
108    ASSERT_EQ(0, pthread_create(&thread, &attr, &thread_stack_tests, 0), "");
109    void* result;
110    ASSERT_EQ(0, pthread_join(thread, &result), "");
111    bool other_thread_ok = (uintptr_t)result;
112    EXPECT_TRUE(other_thread_ok, "");
113
114    END_TEST;
115}
116
117BEGIN_TEST_CASE(stack_tests)
118RUN_TEST(main_thread_stack_tests)
119RUN_TEST(other_thread_stack_tests)
120END_TEST_CASE(stack_tests)
121
122#ifndef BUILD_COMBINED_TESTS
123int main(int argc, char** argv) {
124    return unittest_run_all_tests(argc, argv) ? 0 : -1;
125}
126#endif
127