blob: 136459becf82374e3d4da3f29d1a98425aa1ca20 [file] [log] [blame]
// 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