Index: compiler-rt/cmake/config-ix.cmake =================================================================== --- compiler-rt/cmake/config-ix.cmake +++ compiler-rt/cmake/config-ix.cmake @@ -219,6 +219,7 @@ else() set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} powerpc64le) endif() +set(ALL_SHADOWCALLSTACK_SUPPORTED_ARCH ${X86_64} ${ARM64}) if(APPLE) include(CompilerRTDarwinUtils) @@ -429,6 +430,9 @@ list_intersect(XRAY_SUPPORTED_ARCH ALL_XRAY_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(SHADOWCALLSTACK_SUPPORTED_ARCH + ALL_SHADOWCALLSTACK_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) else() # Architectures supported by compiler-rt libraries. @@ -455,6 +459,8 @@ filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH}) filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH}) filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH}) + filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH + ${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH}) endif() if (MSVC) @@ -612,3 +618,10 @@ else() set(COMPILER_RT_HAS_FUZZER FALSE) endif() + +if (COMPILER_RT_HAS_SANITIZER_COMMON AND SHADOWCALLSTACK_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux|Android") + set(COMPILER_RT_HAS_SHADOWCALLSTACK TRUE) +else() + set(COMPILER_RT_HAS_SHADOWCALLSTACK FALSE) +endif() Index: compiler-rt/test/shadowcallstack/CMakeLists.txt =================================================================== --- compiler-rt/test/shadowcallstack/CMakeLists.txt +++ compiler-rt/test/shadowcallstack/CMakeLists.txt @@ -1,13 +1,21 @@ +set(TEST_ARCH ${SHADOWCALLSTACK_SUPPORTED_ARCH}) + set(SHADOWCALLSTACK_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SHADOWCALLSTACK_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(SHADOWCALLSTACK_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg - ) +foreach(arch ${SHADOWCALLSTACK_SUPPORTED_ARCH}) + set(SANITIZER_COMMON_TEST_TARGET_ARCH ${arch}) + get_test_cc_for_arch(${arch} + SHADOWSTACK_TEST_TARGET_CC SHADOWSTACK_TEST_TARGET_CFLAGS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${arch}/lit.site.cfg) + list(APPEND SHADOWCALLSTACK_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${arch}) +endforeach() add_lit_testsuite(check-shadowcallstack "Running the ShadowCallStack tests" - ${CMAKE_CURRENT_BINARY_DIR}) + ${SHADOWCALLSTACK_TESTSUITES} + DEPENDS ${SANITIZER_COMMON_LIT_TEST_DEPS}) set_target_properties(check-shadowcallstack PROPERTIES FOLDER "Compiler-RT Misc") Index: compiler-rt/test/shadowcallstack/init.c =================================================================== --- compiler-rt/test/shadowcallstack/init.c +++ compiler-rt/test/shadowcallstack/init.c @@ -6,11 +6,16 @@ // Basic smoke test for the runtime +#include "libc_support.h" + #ifdef INCLUDE_RUNTIME #include "minimal_runtime.h" +#elif defined(__aarch64__) +int main(void); +void __shadowcallstack_start() { scs_exit(main()); } #endif -int main(int argc, char **argv) { - printf("In main.\n"); +int main(void) { + scs_fputs_stdout("In main.\n"); return 0; } Index: compiler-rt/test/shadowcallstack/libc_support.h =================================================================== --- /dev/null +++ compiler-rt/test/shadowcallstack/libc_support.h @@ -0,0 +1,98 @@ +// This header provides replacements for certain libc functions. It is necessary +// in order to safely run the tests on aarch64, because the system libc might +// not have been compiled with -ffixed-x18. + +#pragma once + +#include +#include + +#ifdef __aarch64__ + +size_t scs_strlen(const char *p) { + size_t retval = 0; + while (*p++) + retval++; + return retval; +} + +// This function is called before the SCS is allocated, so it cannot use the +// SCS. +__attribute__((no_sanitize("shadow-call-stack"))) void *scs_mmap_anon( + size_t size) { + void *retval; + __asm__ __volatile__( + "mov x0, xzr\n" + "mov x1, %0\n" + "orr w2, wzr, #0x3\n" // PROT_READ | PROT_WRITE + "mov w3, #0x22\n" // MAP_PRIVATE | MAP_ANONYMOUS + "mov w4, #0xffffffff\n" + "mov x5, xzr\n" + "mov x8, #222\n" // mmap + "svc #0\n" + "mov %0, x0\n" + : "=r"(retval) + : "r"(size) + : "x0", "x1", "x2", "x3", "x4", "x5", "x8"); + return retval; +} + +// We mark all of these functions as noinline to make sure that their callers do +// not become leaf functions as a result of inlining. This is because we want to +// make sure that we generate the correct code for non-leaf functions. + +__attribute__((noinline)) void scs_abort(void) { + __asm__ __volatile__( + "mov x8, #172\n" // getpid + "svc #0\n" + "mov x1, #6\n" // SIGABRT + "mov x8, #129\n" // kill + "svc #0\n"); +} + +__attribute__((noinline)) void scs_fputs_stdout(const char *p) { + __asm__ __volatile__( + "mov x0, #1\n" // stdout + "mov x1, %0\n" + "mov x2, %1\n" + "mov x8, #64\n" // write + "svc #0\n" ::"r"(p), + "r"(scs_strlen(p)) + : "x0", "x1", "x2", "x8"); +} + +__attribute__((noinline)) void scs_exit(int exit_code) { + __asm__ __volatile__( + "mov x0, %0\n" + "mov x8, #93\n" // exit + "svc #0\n" ::"r"((size_t)exit_code) + : "x0", "x8"); +} + +#else + +#include +#include +#include +#include +#include + +__attribute__((no_sanitize("shadow-call-stack"))) void *scs_mmap_anon( + size_t size) { + return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); +} + +void scs_abort(void) { + abort(); +} + +void scs_fputs_stdout(const char *p) { + fputs(p, stdout); +} + +void scs_exit(int exit_code) { + exit(exit_code); +} + +#endif Index: compiler-rt/test/shadowcallstack/lit.cfg =================================================================== --- compiler-rt/test/shadowcallstack/lit.cfg +++ compiler-rt/test/shadowcallstack/lit.cfg @@ -12,8 +12,12 @@ config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test'] # Add clang substitutions. -config.substitutions.append( ("%clang_noscs ", config.clang + " -O0 -fno-sanitize=shadow-call-stack ") ) -config.substitutions.append( ("%clang_scs ", config.clang + " -O0 -fsanitize=shadow-call-stack ") ) +config.substitutions.append( ("%clang_noscs ", config.clang + ' -O0 -fno-sanitize=shadow-call-stack ' + config.target_cflags + ' ') ) -if config.host_os not in ['Linux'] or config.target_arch != 'x86_64': +scs_arch_cflags = config.target_cflags +if config.target_arch == 'aarch64': + scs_arch_cflags += ' -ffixed-x18 -ffreestanding -nostdlib -Wl,-e,__shadowcallstack_start' +config.substitutions.append( ("%clang_scs ", config.clang + ' -O0 -fsanitize=shadow-call-stack ' + scs_arch_cflags + ' ') ) + +if config.host_os not in ['Linux'] or config.target_arch not in ['x86_64', 'aarch64']: config.unsupported = True Index: compiler-rt/test/shadowcallstack/lit.site.cfg.in =================================================================== --- compiler-rt/test/shadowcallstack/lit.site.cfg.in +++ compiler-rt/test/shadowcallstack/lit.site.cfg.in @@ -1,5 +1,10 @@ @LIT_SITE_CFG_IN_HEADER@ +# Tool-specific config options. +config.name_suffix = "@SHADOWCALLSTACK_TEST_CONFIG_SUFFIX@" +config.target_cflags = "@SHADOWCALLSTACK_TEST_TARGET_CFLAGS@" +config.target_arch = "@SHADOWCALLSTACK_TEST_TARGET_ARCH@" + # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") Index: compiler-rt/test/shadowcallstack/minimal_runtime.h =================================================================== --- compiler-rt/test/shadowcallstack/minimal_runtime.h +++ compiler-rt/test/shadowcallstack/minimal_runtime.h @@ -1,26 +1,52 @@ // A shadow call stack runtime is not yet included with compiler-rt, provide a -// minimal runtime to allocate a shadow call stack and assign %gs to point at -// it. +// minimal runtime to allocate a shadow call stack and assign an +// architecture-specific register to point at it. #pragma once +#ifdef __x86_64__ #include +#endif #include #include #include +#include "libc_support.h" + int arch_prctl(int code, void *addr); __attribute__((no_sanitize("shadow-call-stack"))) static void __shadowcallstack_init() { - void *stack = mmap(NULL, 8192, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + void *stack = scs_mmap_anon(8192); if (stack == MAP_FAILED) - abort(); + scs_abort(); +#if defined(__x86_64__) if (arch_prctl(ARCH_SET_GS, stack)) - abort(); + scs_abort(); +#elif defined(__aarch64__) + __asm__ __volatile__("mov x18, %0" ::"r"(stack)); +#else +#error Unsupported platform +#endif } + +#ifdef __aarch64__ + +int main(void); + +// On aarch64 we avoid linking the system libc because it might not have been +// compiled with -ffixed-x18. This replacement entry point does all we need. +__attribute__((no_sanitize("shadow-call-stack"))) +void __shadowcallstack_start() { + __shadowcallstack_init(); + scs_exit(main()); +} + +#else + __attribute__((section(".preinit_array"), used)) void (*__shadowcallstack_preinit)(void) = __shadowcallstack_init; + +#endif Index: compiler-rt/test/shadowcallstack/overflow-aarch64.c =================================================================== --- /dev/null +++ compiler-rt/test/shadowcallstack/overflow-aarch64.c @@ -0,0 +1,5 @@ +// See overflow.c for a description. + +// REQUIRES: aarch64-target-arch +// RUN: %clang_scs %S/overflow.c -o %t -DITERATIONS=12 +// RUN: %run %t | FileCheck %S/overflow.c Index: compiler-rt/test/shadowcallstack/overflow-x86_64.c =================================================================== --- /dev/null +++ compiler-rt/test/shadowcallstack/overflow-x86_64.c @@ -0,0 +1,5 @@ +// See overflow.c for a description. + +// REQUIRES: x86_64-target-arch +// RUN: %clang_scs %S/overflow.c -o %t -DITERATIONS=12 +// RUN: not --crash %run %t Index: compiler-rt/test/shadowcallstack/overflow.c =================================================================== --- compiler-rt/test/shadowcallstack/overflow.c +++ compiler-rt/test/shadowcallstack/overflow.c @@ -1,12 +1,19 @@ -// RUN: %clang_noscs %s -o %t -// RUN: %run %t 3 | FileCheck %s -// RUN: %run %t 12 | FileCheck -check-prefix=OVERFLOW_SUCCESS %s +// Test that a stack overflow fails as expected -// RUN: %clang_scs %s -o %t -// RUN: %run %t 3 | FileCheck %s -// RUN: not --crash %run %t 12 +// RUN: %clang_noscs %s -o %t -DITERATIONS=3 +// RUN: %run %t | FileCheck %s +// RUN: %clang_noscs %s -o %t -DITERATIONS=12 +// RUN: %run %t | FileCheck -check-prefix=OVERFLOW_SUCCESS %s -// Test that a stack overflow fails as expected +// RUN: %clang_scs %s -o %t -DITERATIONS=3 +// RUN: %run %t | FileCheck %s + +// The behavioral check for SCS + overflow lives in the tests overflow-x86_64.c +// and overflow-aarch64.c. This is because the expected behavior is different +// between the two platforms. On x86_64 we crash because the comparison between +// the shadow call stack and the regular stack fails. On aarch64 there is no +// comparison, we just load the return address from the shadow call stack. So we +// just expect not to see the output from print_and_exit. #include #include @@ -16,21 +23,17 @@ void print_and_exit(void) { // CHECK-NOT: Stack overflow successful. // OVERFLOW_SUCCESS: Stack overflow successful. - printf("Stack overflow successful.\n"); - exit(0); + scs_fputs_stdout("Stack overflow successful.\n"); + scs_exit(0); } -int main(int argc, char **argv) +int main(void) { - if (argc != 2) - exit(1); - void *addrs[4]; - const int iterations = atoi(argv[1]); - for (int i = 0; i < iterations; i++) + for (int i = 0; i < ITERATIONS; i++) addrs[i] = &print_and_exit; - printf("Returning.\n"); + scs_fputs_stdout("Returning.\n"); return 0; }