summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2015-06-15 21:08:47 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2015-06-15 21:08:47 +0000
commit6296672cc0f467965254c590563f56a069902072 (patch)
tree8e6d0baf69e92cb146303c4c8095e214b1418b00
parent4a73864a73f96930ce3ed1ddc8fc88be1819a7c7 (diff)
downloadcompiler-rt-6296672cc0f467965254c590563f56a069902072.tar.gz
Protection against stack-based memory corruption errors using SafeStack: compiler-rt runtime support library
This patch adds runtime support for the Safe Stack protection to compiler-rt (see http://reviews.llvm.org/D6094 for the detailed description of the Safe Stack). This patch is our implementation of the safe stack on top of compiler-rt. The patch adds basic runtime support for the safe stack to compiler-rt that manages unsafe stack allocation/deallocation for each thread. Original patch by Volodymyr Kuznetsov and others at the Dependable Systems Lab at EPFL; updates and upstreaming by myself. Differential Revision: http://reviews.llvm.org/D6096 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@239763 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--CMakeLists.txt1
-rw-r--r--cmake/config-ix.cmake9
-rw-r--r--lib/CMakeLists.txt3
-rw-r--r--lib/safestack/CMakeLists.txt28
-rw-r--r--lib/safestack/safestack.cc236
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/safestack/CMakeLists.txt29
-rw-r--r--test/safestack/buffer-copy-vla.c26
-rw-r--r--test/safestack/buffer-copy.c25
-rw-r--r--test/safestack/init.c9
-rw-r--r--test/safestack/lit.cfg24
-rw-r--r--test/safestack/lit.site.cfg.in8
-rw-r--r--test/safestack/lto.c12
-rw-r--r--test/safestack/overflow.c23
-rw-r--r--test/safestack/pthread-cleanup.c31
-rw-r--r--test/safestack/pthread.c40
-rw-r--r--test/safestack/utils.h8
17 files changed, 515 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d83796411..7daee7ce8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -207,6 +207,7 @@ append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COM
append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG -fno-sanitize=safe-stack SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index 114c2a5b6..60dae9cf0 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -19,6 +19,7 @@ check_cxx_compiler_flag(-fno-exceptions COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG
check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)
check_cxx_compiler_flag(-funwind-tables COMPILER_RT_HAS_FUNWIND_TABLES_FLAG)
check_cxx_compiler_flag(-fno-stack-protector COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG)
+check_cxx_compiler_flag(-fno-sanitize=safe-stack COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG)
check_cxx_compiler_flag(-fvisibility=hidden COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG)
check_cxx_compiler_flag(-fno-rtti COMPILER_RT_HAS_FNO_RTTI_FLAG)
check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG)
@@ -256,6 +257,7 @@ filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 i686 arm mips mips64
filter_available_targets(TSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips
mipsel mips64 mips64el powerpc64 powerpc64le)
+filter_available_targets(SAFESTACK_SUPPORTED_ARCH x86_64 i386 i686)
if(ANDROID)
set(OS_NAME "Android")
@@ -334,3 +336,10 @@ endif()
if("${LLVM_NATIVE_ARCH}" STREQUAL "Mips")
set(COMPILER_RT_HAS_MSSE3_FLAG FALSE)
endif()
+
+if (SAFESTACK_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+ set(COMPILER_RT_HAS_SAFESTACK TRUE)
+else()
+ set(COMPILER_RT_HAS_SAFESTACK FALSE)
+endif()
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 2b6ae546e..3a5d4297e 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -34,3 +34,6 @@ if(COMPILER_RT_HAS_TSAN)
add_subdirectory(tsan/dd)
endif()
+if(COMPILER_RT_HAS_SAFESTACK)
+ add_subdirectory(safestack)
+endif()
diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt
new file mode 100644
index 000000000..1c15d079d
--- /dev/null
+++ b/lib/safestack/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_custom_target(safestack)
+
+set(SAFESTACK_SOURCES safestack.cc)
+
+include_directories(..)
+
+set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx
+ ARCH ${SAFESTACK_SUPPORTED_ARCH}
+ SOURCES ${SAFESTACK_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ CFLAGS ${SAFESTACK_CFLAGS})
+ add_dependencies(safestack clang_rt.safestack_osx)
+else()
+ # Otherwise, build separate libraries for each target.
+ foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
+ add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC
+ SOURCES ${SAFESTACK_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ CFLAGS ${SAFESTACK_CFLAGS})
+ add_dependencies(safestack clang_rt.safestack-${arch})
+ endforeach()
+endif()
diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc
new file mode 100644
index 000000000..43366fdf7
--- /dev/null
+++ b/lib/safestack/safestack.cc
@@ -0,0 +1,236 @@
+//===-- safestack.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the runtime support for the safe stack protection
+// mechanism. The runtime manages allocation/deallocation of the unsafe stack
+// for the main thread, as well as all pthreads that are created/destroyed
+// during program execution.
+//
+//===----------------------------------------------------------------------===//
+
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <sys/user.h>
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+// TODO: The runtime library does not currently protect the safe stack. The
+// protection of the (safe) stack can be provided by two alternative features
+// that requires C library support:
+//
+// 1) Protection via hardware segmentation on x32 architectures: the (safe)
+// stack segment (implicitly accessed via the %ss segment register) can be
+// separated from the data segment (implicitly accessed via the %ds segment
+// register). Dereferencing a pointer to the safe segment would result in a
+// segmentation fault.
+//
+// 2) Protection via information hiding on 64 bit architectures: the location of
+// the safe stack can be randomized through secure mechanisms, and the leakage
+// of the stack pointer can be prevented. Currently, libc can leak the stack
+// pointer in several ways (e.g. in longjmp, signal handling, user-level context
+// switching related functions, etc.). These can be fixed in libc and in other
+// low-level libraries, by either eliminating the escaping/dumping of the stack
+// pointer (i.e., %rsp) when that's possible, or by using encryption/PTR_MANGLE
+// (XOR-ing the dumped stack pointer with another secret we control and protect
+// better). (This is already done for setjmp in glibc.) Furthermore, a static
+// machine code level verifier can be ran after code generation to make sure
+// that the stack pointer is never written to memory, or if it is, its written
+// on the safe stack.
+//
+// Finally, while the Unsafe Stack pointer is currently stored in a thread local
+// variable, with libc support it could be stored in the TCB (thread control
+// block) as well, eliminating another level of indirection. Alternatively,
+// dedicating a separate register for storing it would also be possible.
+
+/// Minimum stack alignment for the unsafe stack.
+const unsigned kStackAlign = 16;
+
+/// Default size of the unsafe stack. This value is only used if the stack
+/// size rlimit is set to infinity.
+const unsigned kDefaultUnsafeStackSize = 0x2800000;
+
+// TODO: To make accessing the unsafe stack pointer faster, we plan to
+// eventually store it directly in the thread control block data structure on
+// platforms where this structure is pointed to by %fs or %gs. This is exactly
+// the same mechanism as currently being used by the traditional stack
+// protector pass to store the stack guard (see getStackCookieLocation()
+// function above). Doing so requires changing the tcbhead_t struct in glibc
+// on Linux and tcb struct in libc on FreeBSD.
+//
+// For now, store it in a thread-local variable.
+extern "C" {
+__attribute__((visibility(
+ "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr;
+}
+
+// Per-thread unsafe stack information. It's not frequently accessed, so there
+// it can be kept out of the tcb in normal thread-local variables.
+static __thread void *unsafe_stack_start = nullptr;
+static __thread size_t unsafe_stack_size = 0;
+static __thread size_t unsafe_stack_guard = 0;
+
+static inline void *unsafe_stack_alloc(size_t size, size_t guard) {
+ CHECK_GE(size + guard, size);
+ void *addr = MmapOrDie(size + guard, "unsafe_stack_alloc");
+ MprotectNoAccess((uptr)addr, (uptr)guard);
+ return (char *)addr + guard;
+}
+
+static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
+ CHECK_GE((char *)start + size, (char *)start);
+ CHECK_GE((char *)start + guard, (char *)start);
+ void *stack_ptr = (char *)start + size;
+ CHECK_EQ((((size_t)stack_ptr) & (kStackAlign - 1)), 0);
+
+ __safestack_unsafe_stack_ptr = stack_ptr;
+ unsafe_stack_start = start;
+ unsafe_stack_size = size;
+ unsafe_stack_guard = guard;
+}
+
+static void unsafe_stack_free() {
+ if (unsafe_stack_start) {
+ UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard,
+ unsafe_stack_size + unsafe_stack_guard);
+ }
+ unsafe_stack_start = nullptr;
+}
+
+/// Thread data for the cleanup handler
+static pthread_key_t thread_cleanup_key;
+
+/// Safe stack per-thread information passed to the thread_start function
+struct tinfo {
+ void *(*start_routine)(void *);
+ void *start_routine_arg;
+
+ void *unsafe_stack_start;
+ size_t unsafe_stack_size;
+ size_t unsafe_stack_guard;
+};
+
+/// Wrap the thread function in order to deallocate the unsafe stack when the
+/// thread terminates by returning from its main function.
+static void *thread_start(void *arg) {
+ struct tinfo *tinfo = (struct tinfo *)arg;
+
+ void *(*start_routine)(void *) = tinfo->start_routine;
+ void *start_routine_arg = tinfo->start_routine_arg;
+
+ // Setup the unsafe stack; this will destroy tinfo content
+ unsafe_stack_setup(tinfo->unsafe_stack_start, tinfo->unsafe_stack_size,
+ tinfo->unsafe_stack_guard);
+
+ // Make sure out thread-specific destructor will be called
+ // FIXME: we can do this only any other specific key is set by
+ // intercepting the pthread_setspecific function itself
+ pthread_setspecific(thread_cleanup_key, (void *)1);
+
+ return start_routine(start_routine_arg);
+}
+
+/// Thread-specific data destructor
+static void thread_cleanup_handler(void *_iter) {
+ // We want to free the unsafe stack only after all other destructors
+ // have already run. We force this function to be called multiple times.
+ // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
+ // times might still end up executing after the unsafe stack is deallocated.
+ size_t iter = (size_t)_iter;
+ if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
+ pthread_setspecific(thread_cleanup_key, (void *)(iter + 1));
+ } else {
+ // This is the last iteration
+ unsafe_stack_free();
+ }
+}
+
+/// Intercept thread creation operation to allocate and setup the unsafe stack
+INTERCEPTOR(int, pthread_create, pthread_t *thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void*), void *arg) {
+
+ size_t size = 0;
+ size_t guard = 0;
+
+ if (attr != NULL) {
+ pthread_attr_getstacksize(attr, &size);
+ pthread_attr_getguardsize(attr, &guard);
+ } else {
+ // get pthread default stack size
+ pthread_attr_t tmpattr;
+ pthread_attr_init(&tmpattr);
+ pthread_attr_getstacksize(&tmpattr, &size);
+ pthread_attr_getguardsize(&tmpattr, &guard);
+ pthread_attr_destroy(&tmpattr);
+ }
+
+ CHECK_NE(size, 0);
+ CHECK_EQ((size & (kStackAlign - 1)), 0);
+ CHECK_EQ((guard & (PAGE_SIZE - 1)), 0);
+
+ void *addr = unsafe_stack_alloc(size, guard);
+ struct tinfo *tinfo =
+ (struct tinfo *)(((char *)addr) + size - sizeof(struct tinfo));
+ tinfo->start_routine = start_routine;
+ tinfo->start_routine_arg = arg;
+ tinfo->unsafe_stack_start = addr;
+ tinfo->unsafe_stack_size = size;
+ tinfo->unsafe_stack_guard = guard;
+
+ return REAL(pthread_create)(thread, attr, thread_start, tinfo);
+}
+
+extern "C" __attribute__((visibility("default")))
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, the constructor is invoked using .preinit_array (see below)
+__attribute__((constructor(0)))
+#endif
+void __safestack_init() {
+ // Determine the stack size for the main thread.
+ size_t size = kDefaultUnsafeStackSize;
+ size_t guard = 4096;
+
+ struct rlimit limit;
+ if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY)
+ size = limit.rlim_cur;
+
+ // Allocate unsafe stack for main thread
+ void *addr = unsafe_stack_alloc(size, guard);
+
+ unsafe_stack_setup(addr, size, guard);
+
+ // Initialize pthread interceptors for thread allocation
+ INTERCEPT_FUNCTION(pthread_create);
+
+ // Setup the cleanup handler
+ pthread_key_create(&thread_cleanup_key, thread_cleanup_handler);
+}
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, run safestack initialization before any other constructors.
+// On other platforms we use the constructor attribute to arrange to run our
+// initialization early.
+extern "C" {
+__attribute__((section(".preinit_array"),
+ used)) void (*__safestack_preinit)(void) = __safestack_init;
+}
+#endif
+
+extern "C"
+ __attribute__((visibility("default"))) void *__get_unsafe_stack_start() {
+ return unsafe_stack_start;
+}
+
+extern "C"
+ __attribute__((visibility("default"))) void *__get_unsafe_stack_ptr() {
+ return __safestack_unsafe_stack_ptr;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 85a1735b1..ce715bb1d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -58,6 +58,9 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
add_subdirectory(ubsan)
endif()
add_subdirectory(cfi)
+ if(COMPILER_RT_HAS_SAFESTACK)
+ add_subdirectory(safestack)
+ endif()
endif()
if(COMPILER_RT_STANDALONE_BUILD)
diff --git a/test/safestack/CMakeLists.txt b/test/safestack/CMakeLists.txt
new file mode 100644
index 000000000..6f5c2f9b0
--- /dev/null
+++ b/test/safestack/CMakeLists.txt
@@ -0,0 +1,29 @@
+set(SAFESTACK_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(SAFESTACK_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+set(SAFESTACK_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
+if(NOT COMPILER_RT_STANDALONE_BUILD)
+ list(APPEND SAFESTACK_TEST_DEPS safestack)
+
+ # Some tests require LTO, so add a dependency on the relevant LTO plugin.
+ if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR)
+ list(APPEND SAFESTACK_TEST_DEPS
+ LLVMgold
+ )
+ endif()
+ if(APPLE)
+ list(APPEND SAFESTACK_TEST_DEPS
+ LTO
+ )
+ endif()
+endif()
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+add_lit_testsuite(check-safestack "Running the SafeStack tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${SAFESTACK_TEST_DEPS})
+set_target_properties(check-safestack PROPERTIES FOLDER "SafeStack tests")
diff --git a/test/safestack/buffer-copy-vla.c b/test/safestack/buffer-copy-vla.c
new file mode 100644
index 000000000..356a1ac5c
--- /dev/null
+++ b/test/safestack/buffer-copy-vla.c
@@ -0,0 +1,26 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+#include "utils.h"
+
+// Test that loads/stores work correctly for VLAs on the unsafe stack.
+
+int main(int argc, char **argv)
+{
+ int i = 128;
+ break_optimization(&i);
+ char buffer[i];
+
+ // check that we can write to a buffer
+ for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+ buffer[i] = argv[0][i];
+ buffer[i] = '\0';
+
+ break_optimization(buffer);
+
+ // check that we can read from a buffer
+ for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+ if (buffer[i] != argv[0][i])
+ return 1;
+ return 0;
+}
diff --git a/test/safestack/buffer-copy.c b/test/safestack/buffer-copy.c
new file mode 100644
index 000000000..96b2302be
--- /dev/null
+++ b/test/safestack/buffer-copy.c
@@ -0,0 +1,25 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+#include "utils.h"
+
+// Test that loads/stores work correctly for variables on the unsafe stack.
+
+int main(int argc, char **argv)
+{
+ int i;
+ char buffer[128];
+
+ // check that we can write to a buffer
+ for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+ buffer[i] = argv[0][i];
+ buffer[i] = '\0';
+
+ break_optimization(buffer);
+
+ // check that we can read from a buffer
+ for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+ if (buffer[i] != argv[0][i])
+ return 1;
+ return 0;
+}
diff --git a/test/safestack/init.c b/test/safestack/init.c
new file mode 100644
index 000000000..cff75f5f8
--- /dev/null
+++ b/test/safestack/init.c
@@ -0,0 +1,9 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+// Basic smoke test for the runtime library.
+
+int main(int argc, char **argv)
+{
+ return 0;
+}
diff --git a/test/safestack/lit.cfg b/test/safestack/lit.cfg
new file mode 100644
index 000000000..13fc92fa4
--- /dev/null
+++ b/test/safestack/lit.cfg
@@ -0,0 +1,24 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'SafeStack'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test']
+
+# Add clang substitutions.
+config.substitutions.append( ("%clang_nosafestack ", config.clang + " -O0 -fno-sanitize=safe-stack ") )
+config.substitutions.append( ("%clang_safestack ", config.clang + " -O0 -fsanitize=safe-stack ") )
+
+if config.lto_supported:
+ config.available_features.add('lto')
+ config.substitutions.append((r"%clang_lto_safestack ", ' '.join(config.lto_launch + [config.clang] + config.lto_flags + ['-flto -fsanitize=safe-stack '])))
+
+# SafeStack tests are currently supported on Linux, FreeBSD and Darwin only.
+if config.host_os not in ['Linux', 'FreeBSD', 'Darwin']:
+ config.unsupported = True
diff --git a/test/safestack/lit.site.cfg.in b/test/safestack/lit.site.cfg.in
new file mode 100644
index 000000000..cb1e7292e
--- /dev/null
+++ b/test/safestack/lit.site.cfg.in
@@ -0,0 +1,8 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@SAFESTACK_LIT_SOURCE_DIR@/lit.cfg")
diff --git a/test/safestack/lto.c b/test/safestack/lto.c
new file mode 100644
index 000000000..6ee23a1c7
--- /dev/null
+++ b/test/safestack/lto.c
@@ -0,0 +1,12 @@
+// REQUIRES: lto
+
+// RUN: %clang_lto_safestack %s -o %t
+// RUN: %run %t
+
+// Test that safe stack works with LTO.
+
+int main() {
+ char c[] = "hello world";
+ puts(c);
+ return 0;
+}
diff --git a/test/safestack/overflow.c b/test/safestack/overflow.c
new file mode 100644
index 000000000..14e29823c
--- /dev/null
+++ b/test/safestack/overflow.c
@@ -0,0 +1,23 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+// RUN: %clang_nosafestack -fno-stack-protector %s -o %t
+// RUN: not %run %t
+
+// Test that buffer overflows on the unsafe stack do not affect variables on the
+// safe stack.
+
+__attribute__((noinline))
+void fct(volatile int *buffer)
+{
+ memset(buffer - 1, 0, 7 * sizeof(int));
+}
+
+int main(int argc, char **argv)
+{
+ int value1 = 42;
+ int buffer[5];
+ int value2 = 42;
+ fct(buffer);
+ return value1 != 42 || value2 != 42;
+}
diff --git a/test/safestack/pthread-cleanup.c b/test/safestack/pthread-cleanup.c
new file mode 100644
index 000000000..805366c9f
--- /dev/null
+++ b/test/safestack/pthread-cleanup.c
@@ -0,0 +1,31 @@
+// RUN: %clang_safestack %s -pthread -o %t
+// RUN: not --crash %run %t
+
+// Test that unsafe stacks are deallocated correctly on thread exit.
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+enum { kBufferSize = (1 << 15) };
+
+void *t1_start(void *ptr)
+{
+ char buffer[kBufferSize];
+ return buffer;
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t t1;
+ char *buffer = NULL;
+
+ if (pthread_create(&t1, NULL, t1_start, NULL))
+ abort();
+ if (pthread_join(t1, &buffer))
+ abort();
+
+ // should segfault here
+ memset(buffer, 0, kBufferSize);
+ return 0;
+}
diff --git a/test/safestack/pthread.c b/test/safestack/pthread.c
new file mode 100644
index 000000000..1687c10a6
--- /dev/null
+++ b/test/safestack/pthread.c
@@ -0,0 +1,40 @@
+// RUN: %clang_safestack %s -pthread -o %t
+// RUN: %run %t
+
+// Test that pthreads receive their own unsafe stack.
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "utils.h"
+
+static int ptr_test = 42;
+
+void *t1_start(void *ptr)
+{
+ if (ptr != &ptr_test)
+ abort();
+
+ // safe stack
+ int val = ptr_test * 5;
+
+ // unsafe stack
+ char buffer[8096]; // two pages
+ memset(buffer, val, sizeof (buffer));
+ break_optimization(buffer);
+
+ return ptr;
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t t1;
+ void *ptr = NULL;
+ if (pthread_create(&t1, NULL, t1_start, &ptr_test))
+ abort();
+ if (pthread_join(t1, &ptr))
+ abort();
+ if (ptr != &ptr_test)
+ abort();
+ return 0;
+}
diff --git a/test/safestack/utils.h b/test/safestack/utils.h
new file mode 100644
index 000000000..b04e3bdc1
--- /dev/null
+++ b/test/safestack/utils.h
@@ -0,0 +1,8 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+static inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+#endif