shadowcallstack: Make runtime tests compatible with aarch64.

Differential Revision: https://reviews.llvm.org/D45303

llvm-svn: 329614
This commit is contained in:
Peter Collingbourne 2018-04-09 20:18:10 +00:00
parent d9e96741c4
commit 6662e9890b
10 changed files with 136 additions and 31 deletions

View File

@ -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()

View File

@ -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")

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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 <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;
}