Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ 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: test/shadowcallstack/CMakeLists.txt =================================================================== --- test/shadowcallstack/CMakeLists.txt +++ 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: test/shadowcallstack/init.c =================================================================== --- test/shadowcallstack/init.c +++ test/shadowcallstack/init.c @@ -6,11 +6,15 @@ // Basic smoke test for the runtime +#include "libc_support.h" + #ifdef INCLUDE_RUNTIME #include "minimal_runtime.h" +#else +#define scs_main main #endif -int main(int argc, char **argv) { - printf("In main.\n"); +int scs_main(void) { + scs_fputs_stdout("In main.\n"); return 0; } Index: test/shadowcallstack/libc_support.h =================================================================== --- test/shadowcallstack/libc_support.h +++ test/shadowcallstack/libc_support.h @@ -0,0 +1,41 @@ +// 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 +#include + +#ifdef __aarch64__ + +size_t scs_strlen(const char *p) { + size_t retval = 0; + while (*p++) + retval++; + return retval; +} + +// We mark this function as noinline to make sure that its 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_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"); +} + +#else + +__attribute__((noinline)) void scs_fputs_stdout(const char *p) { + fputs(p, stdout); +} + +#endif Index: test/shadowcallstack/lit.cfg =================================================================== --- test/shadowcallstack/lit.cfg +++ 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 ' +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: test/shadowcallstack/lit.site.cfg.in =================================================================== --- test/shadowcallstack/lit.site.cfg.in +++ 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: test/shadowcallstack/minimal_runtime.h =================================================================== --- test/shadowcallstack/minimal_runtime.h +++ test/shadowcallstack/minimal_runtime.h @@ -1,15 +1,18 @@ // 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 +int arch_prctl(int code, void *addr); +#endif #include #include #include -int arch_prctl(int code, void *addr); +#include "libc_support.h" __attribute__((no_sanitize("shadow-call-stack"))) static void __shadowcallstack_init() { @@ -18,9 +21,23 @@ if (stack == MAP_FAILED) abort(); +#if defined(__x86_64__) if (arch_prctl(ARCH_SET_GS, stack)) abort(); +#elif defined(__aarch64__) + __asm__ __volatile__("mov x18, %0" ::"r"(stack)); +#else +#error Unsupported platform +#endif } -__attribute__((section(".preinit_array"), used)) - void (*__shadowcallstack_preinit)(void) = __shadowcallstack_init; +int scs_main(void); + +__attribute__((no_sanitize("shadow-call-stack"))) int main(void) { + __shadowcallstack_init(); + + // We can't simply return scs_main() because scs_main might have corrupted our + // return address for testing purposes (see overflow.c), so we need to exit + // ourselves. + exit(scs_main()); +} Index: test/shadowcallstack/overflow-aarch64.c =================================================================== --- test/shadowcallstack/overflow-aarch64.c +++ 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: test/shadowcallstack/overflow-x86_64.c =================================================================== --- test/shadowcallstack/overflow-x86_64.c +++ 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: test/shadowcallstack/overflow.c =================================================================== --- test/shadowcallstack/overflow.c +++ test/shadowcallstack/overflow.c @@ -1,13 +1,20 @@ -// RUN: %clang_noscs %s -o %t -// RUN: %run %t 3 | FileCheck %s -// RUN: %run %t 12 | FileCheck -check-prefix=OVERFLOW_SUCCESS %s - -// RUN: %clang_scs %s -o %t -// RUN: %run %t 3 | FileCheck %s -// RUN: not --crash %run %t 12 - // Test that a stack overflow fails as expected +// 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 + +// 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"); + scs_fputs_stdout("Stack overflow successful.\n"); exit(0); } -int main(int argc, char **argv) +int scs_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; }