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

Commit

Permalink
Return stack support
Browse files Browse the repository at this point in the history
  • Loading branch information
pzieris committed May 29, 2018
1 parent 487e6a9 commit 4cd84ed
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 2 deletions.
12 changes: 11 additions & 1 deletion cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down Expand Up @@ -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})
Expand Down Expand Up @@ -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}")
Expand Down Expand Up @@ -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()
Expand Down
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 <[email protected]>
//
//===----------------------------------------------------------------------===//
//
// 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 <[email protected]>
//
//===----------------------------------------------------------------------===//
//
// 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 <[email protected]>
//
//===----------------------------------------------------------------------===//
//
// 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
Expand Up @@ -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()
Expand Down

0 comments on commit 4cd84ed

Please sign in to comment.