[HWASan] Intercept setjmp/longjmp on x86_64.
Reviewed By: xiangzhangllvm Differential Revision: https://reviews.llvm.org/D109790
This commit is contained in:
parent
6fb01a9470
commit
750d5fc65c
|
|
@ -15,7 +15,8 @@ set(HWASAN_RTL_SOURCES
|
|||
hwasan_memintrinsics.cpp
|
||||
hwasan_poisoning.cpp
|
||||
hwasan_report.cpp
|
||||
hwasan_setjmp.S
|
||||
hwasan_setjmp_aarch64.S
|
||||
hwasan_setjmp_x86_64.S
|
||||
hwasan_tag_mismatch_aarch64.S
|
||||
hwasan_thread.cpp
|
||||
hwasan_thread_list.cpp
|
||||
|
|
|
|||
|
|
@ -187,13 +187,17 @@ void HwasanTagMismatch(uptr addr, uptr access_info, uptr *registers_frame,
|
|||
RunFreeHooks(ptr); \
|
||||
} while (false)
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
// For both bionic and glibc __sigset_t is an unsigned long.
|
||||
typedef unsigned long __hw_sigset_t;
|
||||
// Setjmp and longjmp implementations are platform specific, and hence the
|
||||
// interception code is platform specific too. As yet we've only implemented
|
||||
// the interception for AArch64.
|
||||
typedef unsigned long long __hw_register_buf[22];
|
||||
// interception code is platform specific too.
|
||||
# if defined(__aarch64__)
|
||||
constexpr size_t kHwRegisterBufSize = 22;
|
||||
# elif defined(__x86_64__)
|
||||
constexpr size_t kHwRegisterBufSize = 8;
|
||||
# endif
|
||||
typedef unsigned long long __hw_register_buf[kHwRegisterBufSize];
|
||||
struct __hw_jmp_buf_struct {
|
||||
// NOTE: The machine-dependent definition of `__sigsetjmp'
|
||||
// assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
|
||||
|
|
@ -210,7 +214,7 @@ struct __hw_jmp_buf_struct {
|
|||
typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
|
||||
typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
|
||||
constexpr unsigned kHwJmpBufMagic = 0x248ACE77;
|
||||
#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
|
||||
#endif // HWASAN_WITH_INTERCEPTORS
|
||||
|
||||
#define ENSURE_HWASAN_INITED() \
|
||||
do { \
|
||||
|
|
|
|||
|
|
@ -49,9 +49,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
|
|||
|
||||
DEFINE_REAL(int, vfork)
|
||||
DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
|
||||
#endif // HWASAN_WITH_INTERCEPTORS
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
|
||||
// Get and/or change the set of blocked signals.
|
||||
extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
|
||||
__hw_sigset_t *__restrict __oset);
|
||||
|
|
@ -67,8 +65,14 @@ extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
|
|||
|
||||
static void __attribute__((always_inline))
|
||||
InternalLongjmp(__hw_register_buf env, int retval) {
|
||||
# if defined(__aarch64__)
|
||||
constexpr size_t kSpIndex = 13;
|
||||
# elif defined(__x86_64__)
|
||||
constexpr size_t kSpIndex = 6;
|
||||
# endif
|
||||
|
||||
// Clear all memory tags on the stack between here and where we're going.
|
||||
unsigned long long stack_pointer = env[13];
|
||||
unsigned long long stack_pointer = env[kSpIndex];
|
||||
// The stack pointer should never be tagged, so we don't need to clear the
|
||||
// tag for this function call.
|
||||
__hwasan_handle_longjmp((void *)stack_pointer);
|
||||
|
|
@ -79,6 +83,7 @@ InternalLongjmp(__hw_register_buf env, int retval) {
|
|||
// Must implement this ourselves, since we don't know the order of registers
|
||||
// in different libc implementations and many implementations mangle the
|
||||
// stack pointer so we can't use it without knowing the demangling scheme.
|
||||
# if defined(__aarch64__)
|
||||
register long int retval_tmp asm("x1") = retval;
|
||||
register void *env_address asm("x0") = &env[0];
|
||||
asm volatile("ldp x19, x20, [%0, #0<<3];"
|
||||
|
|
@ -101,6 +106,26 @@ InternalLongjmp(__hw_register_buf env, int retval) {
|
|||
"br x30;"
|
||||
: "+r"(env_address)
|
||||
: "r"(retval_tmp));
|
||||
# elif defined(__x86_64__)
|
||||
register long int retval_tmp asm("%rsi") = retval;
|
||||
register void *env_address asm("%rdi") = &env[0];
|
||||
asm volatile(
|
||||
// Restore registers.
|
||||
"mov (0*8)(%0),%%rbx;"
|
||||
"mov (1*8)(%0),%%rbp;"
|
||||
"mov (2*8)(%0),%%r12;"
|
||||
"mov (3*8)(%0),%%r13;"
|
||||
"mov (4*8)(%0),%%r14;"
|
||||
"mov (5*8)(%0),%%r15;"
|
||||
"mov (6*8)(%0),%%rsp;"
|
||||
"mov (7*8)(%0),%%rdx;"
|
||||
// Return 1 if retval is 0.
|
||||
"mov $1,%%rax;"
|
||||
"test %1,%1;"
|
||||
"cmovnz %1,%%rax;"
|
||||
"jmp *%%rdx;" ::"r"(env_address),
|
||||
"r"(retval_tmp));
|
||||
# endif
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
|
||||
|
|
@ -139,7 +164,7 @@ INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
|
|||
#undef SIG_BLOCK
|
||||
#undef SIG_SETMASK
|
||||
|
||||
#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
|
||||
# endif // HWASAN_WITH_INTERCEPTORS
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
|
|
@ -158,11 +183,9 @@ void InitializeInterceptors() {
|
|||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
#if defined(__linux__)
|
||||
# if defined(__aarch64__)
|
||||
INTERCEPT_FUNCTION(__libc_longjmp);
|
||||
INTERCEPT_FUNCTION(longjmp);
|
||||
INTERCEPT_FUNCTION(siglongjmp);
|
||||
# endif // __aarch64__
|
||||
INTERCEPT_FUNCTION(vfork);
|
||||
#endif // __linux__
|
||||
INTERCEPT_FUNCTION(pthread_create);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//===-- hwasan_setjmp.S --------------------------------------------------------===//
|
||||
//===-- hwasan_setjmp_aarch64.S -------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
// Hence we have to write this function in assembly.
|
||||
|
||||
.section .text
|
||||
.file "hwasan_setjmp.S"
|
||||
.file "hwasan_setjmp_aarch64.S"
|
||||
|
||||
.global __interceptor_setjmp
|
||||
ASM_TYPE_FUNCTION(__interceptor_setjmp)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
//===-- hwasan_setjmp_x86_64.S --------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// setjmp interceptor for x86_64.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_asm.h"
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS && defined(__x86_64__)
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
|
||||
// We want to save the context of the calling function.
|
||||
// That requires
|
||||
// 1) No modification of the return address by this function.
|
||||
// 2) No modification of the stack pointer by this function.
|
||||
// 3) (no modification of any other saved register, but that's not really going
|
||||
// to occur, and hence isn't as much of a worry).
|
||||
//
|
||||
// There's essentially no way to ensure that the compiler will not modify the
|
||||
// stack pointer when compiling a C function.
|
||||
// Hence we have to write this function in assembly.
|
||||
//
|
||||
// TODO: Handle Intel CET.
|
||||
|
||||
.section .text
|
||||
.file "hwasan_setjmp_x86_64.S"
|
||||
|
||||
.global __interceptor_setjmp
|
||||
ASM_TYPE_FUNCTION(__interceptor_setjmp)
|
||||
__interceptor_setjmp:
|
||||
CFI_STARTPROC
|
||||
xorl %esi, %esi
|
||||
jmp __interceptor_sigsetjmp
|
||||
CFI_ENDPROC
|
||||
ASM_SIZE(__interceptor_setjmp)
|
||||
|
||||
.global __interceptor_sigsetjmp
|
||||
ASM_TYPE_FUNCTION(__interceptor_sigsetjmp)
|
||||
__interceptor_sigsetjmp:
|
||||
CFI_STARTPROC
|
||||
|
||||
// Save callee save registers.
|
||||
mov %rbx, (0*8)(%rdi)
|
||||
mov %rbp, (1*8)(%rdi)
|
||||
mov %r12, (2*8)(%rdi)
|
||||
mov %r13, (3*8)(%rdi)
|
||||
mov %r14, (4*8)(%rdi)
|
||||
mov %r15, (5*8)(%rdi)
|
||||
|
||||
// Save SP as it was in caller's frame.
|
||||
lea 8(%rsp), %rdx
|
||||
mov %rdx, (6*8)(%rdi)
|
||||
|
||||
// Save return address.
|
||||
mov (%rsp), %rax
|
||||
mov %rax, (7*8)(%rdi)
|
||||
|
||||
jmp __sigjmp_save
|
||||
|
||||
CFI_ENDPROC
|
||||
ASM_SIZE(__interceptor_sigsetjmp)
|
||||
|
||||
|
||||
.macro WEAK_ALIAS first second
|
||||
.globl \second
|
||||
.equ \second\(), \first
|
||||
.weak \second
|
||||
.endm
|
||||
|
||||
WEAK_ALIAS __interceptor_sigsetjmp, __sigsetjmp
|
||||
WEAK_ALIAS __interceptor_setjmp, _setjmp
|
||||
#endif
|
||||
|
||||
// We do not need executable stack.
|
||||
NO_EXEC_STACK_DIRECTIVE
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
#define CHECK_TYPE_SIZE_FITS(TYPE) \
|
||||
COMPILER_CHECK(sizeof(__hw_##TYPE) <= sizeof(TYPE))
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
CHECK_TYPE_SIZE_FITS(jmp_buf);
|
||||
CHECK_TYPE_SIZE_FITS(sigjmp_buf);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// RUN: %clang_hwasan -g %s -o %t
|
||||
// RUN: not %run %t 0 2>&1 | FileCheck %s
|
||||
// RUN: not %run %t -33 2>&1 | FileCheck %s
|
||||
// Only implemented for interceptor ABI on AArch64.
|
||||
// REQUIRES: aarch64-target-arch
|
||||
// REQUIRES: pointer-tagging
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
|
|
|
|||
Loading…
Reference in New Issue