Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: llvm-mirror/compiler-rt
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: llvm-return-stack/compiler-rt
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: return-stack
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 1 commit
  • 6 files changed
  • 1 contributor

Commits on May 29, 2018

  1. Return stack support

    pzieris committed May 29, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4cd84ed View commit details
Showing with 344 additions and 2 deletions.
  1. +11 −1 cmake/config-ix.cmake
  2. +24 −0 lib/returnstack/CMakeLists.txt
  3. +217 −0 lib/returnstack/returnstack.cc
  4. +44 −0 lib/returnstack/rsp_routines_aarch64.inc
  5. +46 −0 lib/returnstack/rsp_routines_x86_64.inc
  6. +2 −1 test/CMakeLists.txt
12 changes: 11 additions & 1 deletion cmake/config-ix.cmake
Original file line number Diff line number Diff line change
@@ -207,6 +207,7 @@ set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64})
set(ALL_RETURNSTACK_SUPPORTED_ARCH ${X86_64} ${ARM64})
set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS64})
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64})
set(ALL_SCUDO_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64})
@@ -451,6 +452,8 @@ else()
filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH})
filter_available_targets(SAFESTACK_SUPPORTED_ARCH
${ALL_SAFESTACK_SUPPORTED_ARCH})
filter_available_targets(RETURNSTACK_SUPPORTED_ARCH
${ALL_RETURNSTACK_SUPPORTED_ARCH})
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH})
filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH})
@@ -484,7 +487,7 @@ else()
set(OS_NAME "${CMAKE_SYSTEM_NAME}")
endif()

set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;esan;scudo;ubsan_minimal)
set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;returnstack;cfi;esan;scudo;ubsan_minimal)
set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
"sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -581,6 +584,13 @@ else()
set(COMPILER_RT_HAS_SAFESTACK FALSE)
endif()

if (COMPILER_RT_HAS_SANITIZER_COMMON AND RETURNSTACK_SUPPORTED_ARCH AND
OS_NAME MATCHES "Linux")
set(COMPILER_RT_HAS_RETURNSTACK TRUE)
else()
set(COMPILER_RT_HAS_RETURNSTACK FALSE)
endif()

if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH)
set(COMPILER_RT_HAS_CFI TRUE)
else()
24 changes: 24 additions & 0 deletions lib/returnstack/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
add_compiler_rt_component(returnstack)

set(RETURNSTACK_SOURCES returnstack.cc)

include_directories(..)

set(RETURNSTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})

if (COMPILER_RT_DEFAULT_TARGET_ARCH STREQUAL "aarch64")
list(APPEND RETURNSTACK_CFLAGS "-ffixed-x28")
elseif (COMPILER_RT_DEFAULT_TARGET_ARCH STREQUAL "x86_64")
list(APPEND RETURNSTACK_CFLAGS "-ffixed-r15")
endif ()

foreach(arch ${RETURNSTACK_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.returnstack
STATIC
ARCHS ${arch}
SOURCES ${RETURNSTACK_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
CFLAGS ${RETURNSTACK_CFLAGS}
PARENT_TARGET returnstack)
endforeach()

217 changes: 217 additions & 0 deletions lib/returnstack/returnstack.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
//===-- returnstack.cc ----------------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Author: Philipp Zieris <philipp.zieris@aisec.fraunhofer.de>
//
//===----------------------------------------------------------------------===//
//
// This file implements the runtime support for the return stack protection
// mechanism. The runtime manages allocation/deallocation of return stacks
// for the main thread, as well as all pthreads that are created/destroyed
// during program execution.
//
//===----------------------------------------------------------------------===//

#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h>
#include <unistd.h>

#include "interception/interception.h"

using namespace __sanitizer;

#if defined(__aarch64__)
#include "rsp_routines_aarch64.inc"
#elif defined(__x86_64__)
#include "rsp_routines_x86_64.inc"
#else
#error "Return stack runtime support not available for this architecture."
#endif

/// Page size obtained at runtime.
static unsigned pageSize;

/// Base address of the return stack region (not a secret information).
static uptr ReturnStackRegionBase;

/// The size of the return stack region is dependant on the architecture's user
/// space size. The maximum return stack region size is 16 TB, which results in
/// 32 entropy bits for randomizing return stacks.
///
/// Architecture User space Return stack region Entropy
/// ARM64 256 TB 16 TB 32 bits
/// x86-64 128 TB 16 TB 32 bits
#if defined(__aarch64__)
const unsigned long kReturnStackRegionSize = 0x100000000000UL;
#elif defined(__x86_64__)
const unsigned long kReturnStackRegionSize = 0x100000000000UL;
#endif

/// Number of return stack pages.
const unsigned kReturnStackPages = 8;

/// Number of guard pages.
const unsigned kReturnStackGuardPages = 1;

/// Marker placed on the return stack between metadata and return addresses.
#if SANITIZER_WORDSIZE == 64
const unsigned long kReturnStackMarker = 0xffffffffffffffff;
#else
const unsigned long kReturnStackMarker = 0xffffffff;
#endif

typedef struct thread_start {
void *(*start_routine)(void *);
void *arg;
} thread_start_t;

static void NORETURN terminate(char const *message) {
fprintf(stderr, "Return stack runtime error: %s.\n", message);
exit(EXIT_FAILURE);
}

static inline void return_stack_region_create() {
uptr addr;

// Allocate the return stack region.
addr = (uptr)mmap(0, kReturnStackRegionSize, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == (uptr)-1)
terminate("Failed to allocate the return stack region");

ReturnStackRegionBase = addr;
}

static inline bool contains_return_stack(uptr addr, size_t size) {
int fd[2];
uptr end;

// Open pipe for writing.
if (pipe(fd) == -1)
terminate("Failed to open pipe");

// Try to read from each page within the range.
end = addr + size;
while (addr < end) {
if (write(fd[1], (void *)addr, 1) != 1)
return false;
addr += pageSize;
}

return true;
}

static void return_stack_create() {
size_t guardsize, stacksize;
uptr addr;
ssize_t len = sizeof(uptr);

// Calculate guard and stack sizes.
guardsize = kReturnStackGuardPages * pageSize;
stacksize = kReturnStackPages * pageSize;

// Randomly choose a new return stack and set its access permissions.
while (1) {
if (getrandom(&addr, len, 0) < len)
terminate("Failed to get random offset");
addr %= kReturnStackRegionSize - stacksize - guardsize;
addr &= ~((uptr)pageSize - 1);
addr += ReturnStackRegionBase;
if (!contains_return_stack(addr, stacksize + 2 * guardsize)) {
if (mprotect((void *)(addr + guardsize), stacksize, PROT_READ | PROT_WRITE) == -1)
terminate("Failed to set access permissions for return stack");
break;
}
}

// Write base address of return stack to the RSP register.
asm_write_rsp((void *)(addr + guardsize));
addr = 0;

// Push stack size and marker.
asm_push_rsp(stacksize);
asm_push_rsp(kReturnStackMarker);
}

static void return_stack_destroy(void *arg) {
size_t stacksize;
uptr addr;

// Unwind return stack until the marker is hit.
asm_unwind_rsp(kReturnStackMarker);

// Read stack size and base address.
asm_pop_rsp((size_t)stacksize);
asm_read_rsp((uptr)addr);

// Remove access permissions from return stack.
if (mprotect((void *)addr, stacksize, PROT_NONE) == -1)
terminate("Failed to remove access permissions from return stack.");
}

static void *thread_start(void *ts) {

void *(*start_routine)(void *) = ((thread_start_t *)ts)->start_routine;
void *arg = ((thread_start_t *)ts)->arg;

memset(ts, 0, sizeof(thread_start_t));
free(ts);

// Create return stack for the new thread.
return_stack_create();

// Push our clean-up handler on the thread-cancellation stack.
pthread_cleanup_push(return_stack_destroy, NULL);

// Call the actual start routine.
start_routine(arg);

// Pop our clean-up handler.
pthread_cleanup_pop(NULL);

return NULL;
}

INTERCEPTOR(int, pthread_create, pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg) {

// This memory is freed by thread_start.
thread_start_t *ts = (thread_start_t *)malloc(sizeof(thread_start_t));
if (ts == NULL)
terminate("Malloc failure");
memset(ts, 0, sizeof(thread_start_t));
ts->start_routine = start_routine;
ts->arg = arg;

return REAL(pthread_create)(thread, attr, thread_start, ts);
}

extern "C"
__attribute__((visibility("default"))) void __return_stack_init() {

// Get the page size.
if ((pageSize = sysconf(_SC_PAGESIZE)) == (unsigned)-1)
terminate("Failed to retrieve page size");

// Create the return stack region and allocate a return stack for the main
// thread.
return_stack_region_create();
return_stack_create();

// Initialize the pthread interceptor for thread allocation.
INTERCEPT_FUNCTION(pthread_create);
}

extern "C" {
__attribute__((section(".preinit_array"), used))
void (*__returnstack_preinit)(void) = __return_stack_init;
}

44 changes: 44 additions & 0 deletions lib/returnstack/rsp_routines_aarch64.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===-- rsp_routines_aarch64.inc ------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Author: Philipp Zieris <philipp.zieris@aisec.fraunhofer.de>
//
//===----------------------------------------------------------------------===//
//
// This file provides macros for return stack manipulation on ARM64.
//
//===----------------------------------------------------------------------===//

#define asm_pop_rsp(value) \
asm volatile ("ldr %0, [x28, #-8]!" \
: "=r" (value) \
: /* No inputs */ \
: "x28")

#define asm_push_rsp(value) \
asm volatile ("str %0, [x28], #8" \
: /* No outputs */ \
: "r" (value) \
: "x28")

#define asm_read_rsp(rsp) \
asm volatile ("mov %0, x28" \
: "=r" (rsp))

#define asm_unwind_rsp(marker) \
asm volatile ("rs_unwind:" \
"ldr x0, [x28, #-8]!;" \
"cmp x0, %0;" \
"bne rs_unwind;" \
: /* No outputs */ \
: "r" (marker) \
: "cc", "x0", "x28")

#define asm_write_rsp(rsp) \
asm volatile ("mov x28, %0" \
: /* No outputs */ \
: "r" (rsp) \
: "x28");

46 changes: 46 additions & 0 deletions lib/returnstack/rsp_routines_x86_64.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===-- rsp_routines_x86_64.inc -------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Author: Philipp Zieris <philipp.zieris@aisec.fraunhofer.de>
//
//===----------------------------------------------------------------------===//
//
// This file provides macros for return stack manipulation on x86-64.
//
//===----------------------------------------------------------------------===//

#define asm_pop_rsp(value) \
asm volatile ("lea -0x8(%%r15), %%r15;" \
"mov (%%r15), %0;" \
: "=r" (value) \
: /* No inputs */ \
: "r15")

#define asm_push_rsp(value) \
asm volatile ("mov %0, (%%r15);" \
"lea 0x8(%%r15), %%r15;" \
: /* No outputs */ \
: "r" (value) \
: "r15")

#define asm_read_rsp(rsp) \
asm volatile ("movq %%r15, %0" \
: "=r" (rsp))

#define asm_unwind_rsp(marker) \
asm volatile ("rs_unwind:" \
"lea -0x8(%%r15), %%r15;" \
"cmp (%%r15), %0;" \
"jne rs_unwind;" \
: /* No outputs */ \
: "r" (marker) \
: "cc", "r15")

#define asm_write_rsp(rsp) \
asm volatile ("mov %0, %%r15" \
: /* No outputs */ \
: "r" (rsp) \
: "r15")

3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -68,7 +68,8 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)

foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD})
# cfi testing is gated on ubsan
if(NOT ${sanitizer} STREQUAL cfi)
# no testing for return stacks yet
if(NOT ${sanitizer} STREQUAL cfi AND NOT ${sanitizer} STREQUAL returnstack)
compiler_rt_test_runtime(${sanitizer})
endif()
endforeach()