summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/config-ix.cmake13
-rw-r--r--test/shadowcallstack/CMakeLists.txt18
-rw-r--r--test/shadowcallstack/init.c8
-rw-r--r--test/shadowcallstack/libc_support.h41
-rw-r--r--test/shadowcallstack/lit.cfg10
-rw-r--r--test/shadowcallstack/lit.site.cfg.in5
-rw-r--r--test/shadowcallstack/minimal_runtime.h27
-rw-r--r--test/shadowcallstack/overflow-aarch64.c5
-rw-r--r--test/shadowcallstack/overflow-x86_64.c5
-rw-r--r--test/shadowcallstack/overflow.c33
10 files changed, 135 insertions, 30 deletions
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index cdeae90d0..8d3257992 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -219,6 +219,7 @@ set(ALL_XRAY_SUPPORTED_ARCH ${X86_64})
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 @@ if(APPLE)
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 @@ else()
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 @@ if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND
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()
diff --git a/test/shadowcallstack/CMakeLists.txt b/test/shadowcallstack/CMakeLists.txt
index f8fd835be..ab2b18819 100644
--- a/test/shadowcallstack/CMakeLists.txt
+++ b/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")
diff --git a/test/shadowcallstack/init.c b/test/shadowcallstack/init.c
index 8dad6bdbb..f84f38d71 100644
--- a/test/shadowcallstack/init.c
+++ b/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;
}
diff --git a/test/shadowcallstack/libc_support.h b/test/shadowcallstack/libc_support.h
new file mode 100644
index 000000000..5d89aab64
--- /dev/null
+++ b/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 <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#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
diff --git a/test/shadowcallstack/lit.cfg b/test/shadowcallstack/lit.cfg
index 1642bdc10..313cd2b8e 100644
--- a/test/shadowcallstack/lit.cfg
+++ b/test/shadowcallstack/lit.cfg
@@ -12,8 +12,12 @@ config.test_source_root = os.path.dirname(__file__)
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
diff --git a/test/shadowcallstack/lit.site.cfg.in b/test/shadowcallstack/lit.site.cfg.in
index 4582f36d7..aa8913e84 100644
--- a/test/shadowcallstack/lit.site.cfg.in
+++ b/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")
diff --git a/test/shadowcallstack/minimal_runtime.h b/test/shadowcallstack/minimal_runtime.h
index 75d95ec3c..f36fa5a7d 100644
--- a/test/shadowcallstack/minimal_runtime.h
+++ b/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 <asm/prctl.h>
+int arch_prctl(int code, void *addr);
+#endif
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
-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 @@ static void __shadowcallstack_init() {
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());
+}
diff --git a/test/shadowcallstack/overflow-aarch64.c b/test/shadowcallstack/overflow-aarch64.c
new file mode 100644
index 000000000..8da798164
--- /dev/null
+++ b/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
diff --git a/test/shadowcallstack/overflow-x86_64.c b/test/shadowcallstack/overflow-x86_64.c
new file mode 100644
index 000000000..38bb13a96
--- /dev/null
+++ b/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
diff --git a/test/shadowcallstack/overflow.c b/test/shadowcallstack/overflow.c
index b7b29a117..8c3d50c59 100644
--- a/test/shadowcallstack/overflow.c
+++ b/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 <stdio.h>
#include <stdlib.h>
@@ -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;
}