[Tizen] Implement ASan wrapper for Linux ARM32
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Wed, 16 Jun 2021 13:19:36 +0000 (16:19 +0300)
committerGleb Balykov <g.balykov@samsung.com>
Tue, 27 Sep 2022 12:50:22 +0000 (15:50 +0300)
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.

Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
Signed-off-by: Slava Barinov <v.barinov@samsung.com>
src/coreclr/vm/CMakeLists.txt
src/coreclr/vm/arm/stubs.cpp
src/coreclr/vm/arm/tizenasanenv.S [new file with mode: 0644]
src/coreclr/vm/dllimport.cpp
src/coreclr/vm/tizenasanenv.cpp [new file with mode: 0644]
src/coreclr/vm/tizenasanenv.h [new file with mode: 0644]

index 9b10c73..2d943ca 100644 (file)
@@ -727,6 +727,14 @@ elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
 endif()
 
 else(CLR_CMAKE_TARGET_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
@@ -762,6 +770,11 @@ else(CLR_CMAKE_TARGET_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
index 6e32957..cc646f3 100644 (file)
 #include "ecall.h"
 #include "threadsuspend.h"
 
+#if defined(TIZEN_ASAN_ENVIRONMENT) && !defined(CROSS_COMPILE) && !defined(DACCESS_COMPILE)
+#include <tizenasanenv.h>
+#endif
+
 // target write barriers
 EXTERN_C void JIT_WriteBarrier(Object **dst, Object *ref);
 EXTERN_C void JIT_WriteBarrier_End();
@@ -2017,6 +2021,10 @@ class UMEntryThunk * UMEntryThunk::Decode(void *pCallback)
 
 void UMEntryThunkCode::Encode(UMEntryThunkCode *pEntryThunkCodeRX, 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/coreclr/vm/arm/tizenasanenv.S b/src/coreclr/vm/arm/tizenasanenv.S
new file mode 100644 (file)
index 0000000..a8d6a83
--- /dev/null
@@ -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/coreclr/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
index 925e1f7..8f6640b 100644 (file)
 #include "compile.h"
 #endif // FEATURE_PREJIT
 
+#ifdef TIZEN_ASAN_ENVIRONMENT
+#include <tizenasanenv.h>
+#endif // TIZEN_ASAN_ENVIRONMENT
+
 #include "eventtrace.h"
 
 namespace
@@ -5612,6 +5616,13 @@ namespace
         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/coreclr/vm/tizenasanenv.cpp b/src/coreclr/vm/tizenasanenv.cpp
new file mode 100644 (file)
index 0000000..8ffeefa
--- /dev/null
@@ -0,0 +1,174 @@
+#include <string.h>
+#include "common.h"
+#include "tizenasanenv.h"
+
+
+template <typename Type, int STACK_SIZE>
+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 <pshpack1.h>
+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<ReturnInfo, MAX_STACK_DEPTH> 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/coreclr/vm/tizenasanenv.h b/src/coreclr/vm/tizenasanenv.h
new file mode 100644 (file)
index 0000000..df74d91
--- /dev/null
@@ -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_