From 865d7597c2476f339605bd07a3ad70f15187b0ac Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Mon, 22 Jul 2019 13:40:47 +0300 Subject: [Tizen] Implement ASan wrapper for Linux ARM32 This commit implements wrappers that allow interception transitions from managed to external unmanaged code (CIL -> native) and back (native -> CIL). This allows enable/disable ASan during transitions. Due to this, we sanitize only external code, which allows us to achieve acceptable performance. Change-Id: I53ecdc14d28f7210cd9e7f5bd4db0c8ef5ed81fc Signed-off-by: Vyacheslav Cherkashin --- src/vm/CMakeLists.txt | 13 ++++ src/vm/arm/stubs.cpp | 8 +++ src/vm/arm/tizenasanenv.S | 61 ++++++++++++++++ src/vm/dllimport.cpp | 11 +++ src/vm/tizenasanenv.cpp | 174 ++++++++++++++++++++++++++++++++++++++++++++++ src/vm/tizenasanenv.h | 11 +++ 6 files changed, 278 insertions(+) create mode 100644 src/vm/arm/tizenasanenv.S create mode 100644 src/vm/tizenasanenv.cpp create mode 100644 src/vm/tizenasanenv.h diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 3a556aff2f..3f37720742 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -714,6 +714,14 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64) endif() else(WIN32) + if (TIZEN_ASAN_ENVIRONMENT) + list(APPEND VM_SOURCES_WKS + tizenasanenv.cpp + ) + list(APPEND VM_HEADERS_WKS + tizenasanenv.h + ) + endif() if(CLR_CMAKE_TARGET_ARCH_AMD64) set(VM_SOURCES_WKS_ARCH_ASM @@ -750,6 +758,11 @@ else(WIN32) ${ARCH_SOURCES_DIR}/patchedcode.S ${ARCH_SOURCES_DIR}/pinvokestubs.S ) + if (TIZEN_ASAN_ENVIRONMENT) + list(APPEND VM_SOURCES_WKS_ARCH_ASM + ${ARCH_SOURCES_DIR}/tizenasanenv.S + ) + endif() elseif(CLR_CMAKE_TARGET_ARCH_ARM64) set(VM_SOURCES_WKS_ARCH_ASM ${ARCH_SOURCES_DIR}/asmhelpers.S diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp index 6da58b5326..078a16cb8f 100644 --- a/src/vm/arm/stubs.cpp +++ b/src/vm/arm/stubs.cpp @@ -26,6 +26,10 @@ #include "ecall.h" #include "threadsuspend.h" +#if defined(TIZEN_ASAN_ENVIRONMENT) && !defined(CROSS_COMPILE) && !defined(DACCESS_COMPILE) +#include +#endif + // target write barriers EXTERN_C void JIT_WriteBarrier(Object **dst, Object *ref); EXTERN_C void JIT_WriteBarrier_End(); @@ -2490,6 +2494,10 @@ class UMEntryThunk * UMEntryThunk::Decode(void *pCallback) void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam) { +#if defined(TIZEN_ASAN_ENVIRONMENT) && !defined(CROSS_COMPILE) && !defined(DACCESS_COMPILE) + pTargetCode = (BYTE *)TizenASanEnv::CreateWrapperILCode((LPVOID)pTargetCode); +#endif + // ldr r12, [pc + 8] m_code[0] = 0xf8df; m_code[1] = 0xc008; diff --git a/src/vm/arm/tizenasanenv.S b/src/vm/arm/tizenasanenv.S new file mode 100644 index 0000000000..6a8adb94fe --- /dev/null +++ b/src/vm/arm/tizenasanenv.S @@ -0,0 +1,61 @@ +.macro PUSH_REGS + push {r0-r12} + vpush.64 {d0-d7} +.endm + +.macro POP_REGS + vpop.64 {d0-d7} + pop {r0-r12} +.endm + + +// Export symbols +.global tizenASanWrapper +.global tizenASanWrapperSize +.global tizenASanWrapperEntryOffset + +.text +.arm + +tizenASanWrapper: +// !!! ATTENTION !!! +// Don't move this labels (target, pushAddr, popAddr) +// because they mapped to AuxiliaryCalls struct from src/vm/tizenasanenv.cpp +target: .word 0xdeadc0de +pushAddr: .word 0xdeadc0de @ void pushAddr(LPVOID addr) +popAddr: .word 0xdeadc0de @ LPVOID popAddr() + +entryPointer: + // Save context + PUSH_REGS + + // Save the return address and call 'pre handler' + mov r0, lr + ldr r1, pushAddr + blx r1 + + // Restore context + POP_REGS + + // Change the return address + adr lr, postLabel + + // Call original function + ldr pc, target +postLabel: + // Save context + PUSH_REGS + + // Get the return address and call 'post handler' + ldr r0, popAddr + blx r0 + + // Restore the return address + mov lr, r0 + + // Restore context + POP_REGS + bx lr + +tizenASanWrapperSize: .word . - tizenASanWrapper +tizenASanWrapperEntryOffset: .word entryPointer - tizenASanWrapper diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp index 685ea03309..d3c4874af5 100644 --- a/src/vm/dllimport.cpp +++ b/src/vm/dllimport.cpp @@ -46,6 +46,10 @@ #include "compile.h" #endif // FEATURE_PREJIT +#ifdef TIZEN_ASAN_ENVIRONMENT +#include +#endif // TIZEN_ASAN_ENVIRONMENT + #include "eventtrace.h" #include "clr/fs/path.h" using namespace clr::fs; @@ -6696,6 +6700,13 @@ VOID NDirect::NDirectLink(NDirectMethodDesc *pMD) LPVOID pvTarget = NDirectGetEntryPoint(pMD, hmod); if (pvTarget) { +#ifdef TIZEN_ASAN_ENVIRONMENT + if (PAL_IsSanitizedLibraryDirect(hmod)) + { + pvTarget = TizenASanEnv::CreateWrapperSanitizedEntryPoint(pvTarget); + } +#endif // TIZEN_ASAN_ENVIRONMENT + pMD->SetNDirectTarget(pvTarget); fSuccess = TRUE; } diff --git a/src/vm/tizenasanenv.cpp b/src/vm/tizenasanenv.cpp new file mode 100644 index 0000000000..8ffeefad8b --- /dev/null +++ b/src/vm/tizenasanenv.cpp @@ -0,0 +1,174 @@ +#include +#include "common.h" +#include "tizenasanenv.h" + + +template +class StaticStack { + // We don't create constructor because + // this class is used in a zeroed memory area +public: + void push(Type addr) + { + _ASSERTE(m_pos < STACK_SIZE); + + m_data[m_pos++] = addr; + } + + void pop() + { + _ASSERTE(m_pos > 0); + --m_pos; + } + + Type top() + { + _ASSERTE(m_pos > 0); + + return m_data[m_pos - 1]; + } + + bool empty() + { + return m_pos == 0; + } + +private: + int m_pos; + Type m_data[STACK_SIZE]; +}; + +#include +struct AuxiliaryCalls { + LPVOID target; + void (*pushAddr)(LPVOID addr); + LPVOID (*popAddr)(); +}; + +struct ReturnInfo { + LPVOID addr; + bool isSanitized; +}; + +extern "C" void __sanitizer_disable_interceptors(); +extern "C" void __sanitizer_enable_interceptors(); +extern "C" bool __sanitizer_interceptors_are_enabled(); + +extern LPVOID tizenASanWrapper; +extern UINT32 tizenASanWrapperSize; +extern UINT32 tizenASanWrapperEntryOffset; + +// The maximum nesting of transitions between managed and unmanaged code that we support. +// This number is estimated from the common sense. We think this is enough to check any +// sane code (if it is not recursive) and it won't bloat TLS. We do not use dynamic +// allocation because it complicates the process of memory management in TLS variables. +// It is used only for firmware with ASan and will not affect the release version. +#define MAX_STACK_DEPTH 128 +static __thread StaticStack s_retInfoStack; + + +static void DoEnable() +{ + _ASSERTE(__sanitizer_interceptors_are_enabled() == false); + __sanitizer_enable_interceptors(); +} + +static void DoDisable() +{ + _ASSERTE(__sanitizer_interceptors_are_enabled() == true); + __sanitizer_disable_interceptors(); +} + +static void PushAndEnableASan(LPVOID addr) +{ + _ASSERTE(__sanitizer_interceptors_are_enabled() == false); + + ReturnInfo retInfo = { + .addr = addr, + .isSanitized = false, + }; + + s_retInfoStack.push(retInfo); + DoEnable(); +} + +static LPVOID PopAndDisableASan() +{ + _ASSERTE(__sanitizer_interceptors_are_enabled() == true); + + ReturnInfo retInfo = s_retInfoStack.top(); + s_retInfoStack.pop(); + + _ASSERTE(retInfo.isSanitized == false); + DoDisable(); + + return retInfo.addr; +} + +static void PushAndMayBeDisableASan(LPVOID addr) +{ + ReturnInfo retInfo = { + .addr = addr, + .isSanitized = __sanitizer_interceptors_are_enabled(), + }; + + if (retInfo.isSanitized) + DoDisable(); + + s_retInfoStack.push(retInfo); +} + +static LPVOID PopAndMayBeEnableASan() +{ + _ASSERTE(__sanitizer_interceptors_are_enabled() == false); + + ReturnInfo retInfo = s_retInfoStack.top(); + s_retInfoStack.pop(); + + if (retInfo.isSanitized) + DoEnable(); + + return retInfo.addr; +} + +static LPVOID CreateWrapper(LPVOID target, void (*pushAddr)(LPVOID addr), LPVOID (*popAddr)()) +{ + _ASSERTE(tizenASanWrapperEntryOffset == sizeof(AuxiliaryCalls)); + + LPVOID wrapperSpace = (LPVOID)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(tizenASanWrapperSize)); + + AuxiliaryCalls calls = { + .target = target, + .pushAddr = pushAddr, + .popAddr = popAddr, + }; + + // copy auxiliary calls + memcpy(wrapperSpace, &calls, sizeof(calls)); + + LPVOID entryPointer = (LPVOID)((UINT_PTR)wrapperSpace + tizenASanWrapperEntryOffset); + LPVOID wrapperEntryPointer = (LPVOID)((UINT_PTR)&tizenASanWrapper + tizenASanWrapperEntryOffset); + UINT32 wrapperCodeSize = tizenASanWrapperSize - tizenASanWrapperEntryOffset; + + // copy executable code wrapper + memcpy(entryPointer, wrapperEntryPointer, wrapperCodeSize); + + FlushInstructionCache(GetCurrentProcess(), wrapperSpace, tizenASanWrapperSize); + + return entryPointer; +} + + +namespace TizenASanEnv { + +LPVOID CreateWrapperSanitizedEntryPoint(LPVOID target) +{ + return CreateWrapper(target, PushAndEnableASan, PopAndDisableASan); +} + +LPVOID CreateWrapperILCode(LPVOID target) +{ + return CreateWrapper(target, PushAndMayBeDisableASan, PopAndMayBeEnableASan); +} + +} // namespace TizenASanEnv diff --git a/src/vm/tizenasanenv.h b/src/vm/tizenasanenv.h new file mode 100644 index 0000000000..df74d91a3b --- /dev/null +++ b/src/vm/tizenasanenv.h @@ -0,0 +1,11 @@ +#ifndef TIZENASANENV_H_ +#define TIZENASANENV_H_ + +namespace TizenASanEnv { + +LPVOID CreateWrapperSanitizedEntryPoint(LPVOID target); +LPVOID CreateWrapperILCode(LPVOID target); + +} // namespace TizenASanEnv + +#endif // TIZENASANENV_H_ -- cgit v1.2.3