From 1cc4406358675213f66997b3fa1f5b4f70a0d742 Mon Sep 17 00:00:00 2001 From: Kubo Takehiro Date: Mon, 13 Jun 2016 19:49:05 +0900 Subject: [PATCH] Reduce the minimum required hot patching size from 14 to 5 on x86_64 if the distance from a hot-patch target function to the trampoline memory is within 2GB. --- include/hot_patch.h | 4 +- src/audit_plugin.cc | 28 +++++++++--- src/hot_patch.cc | 106 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 112 insertions(+), 26 deletions(-) diff --git a/include/hot_patch.h b/include/hot_patch.h index 36b39e5..e3c1252 100644 --- a/include/hot_patch.h +++ b/include/hot_patch.h @@ -14,9 +14,7 @@ #define GETPAGESIZE() sysconf (_SC_PAGE_SIZE) -unsigned int jump_size(); - -int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, bool log_info); +int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool log_info); void remove_hot_patch_function(void* targetFunction, void* trampolineFunction, unsigned int trampolinesize, bool log_info); diff --git a/src/audit_plugin.cc b/src/audit_plugin.cc index 9241e6f..dd684b0 100644 --- a/src/audit_plugin.cc +++ b/src/audit_plugin.cc @@ -1317,8 +1317,9 @@ static int do_hot_patch(void ** trampoline_func_pp, unsigned int * trampoline_si *trampoline_func_pp = (void*)(addrs & ~0x0F); // hot patch functions + unsigned int used_size; int res = hot_patch_function(target_function, audit_function, - *trampoline_func_pp, trampoline_size, true); + *trampoline_func_pp, trampoline_size, &used_size, true); if (res != 0) { // hot patch failed. @@ -1327,9 +1328,9 @@ static int do_hot_patch(void ** trampoline_func_pp, unsigned int * trampoline_si return 1; } sql_print_information( - "%s hot patch for: %s (%p) complete. Audit func: %p, Trampoline address: %p size: %u.", - log_prefix, func_name, target_function, audit_function, *trampoline_func_pp, *trampoline_size); - trampoline_mem_free = (void *)(((DATATYPE_ADDRESS)*trampoline_func_pp) + *trampoline_size + jump_size()); + "%s hot patch for: %s (%p) complete. Audit func: %p, Trampoline address: %p, size: %u, used: %u.", + log_prefix, func_name, target_function, audit_function, *trampoline_func_pp, *trampoline_size, used_size); + trampoline_mem_free = (void *)(((DATATYPE_ADDRESS)*trampoline_func_pp) + used_size); return 0; } @@ -1614,7 +1615,22 @@ static int audit_plugin_init(void *p) // align our trampoline mem on its own page const unsigned long page_size = GETPAGESIZE(); const unsigned long std_page_size = 4096; - if (page_size <= std_page_size) + bool use_static_memory = (page_size <= std_page_size); + int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS; + +#ifdef __x86_64__ + size_t func_in_mysqld = (size_t)log_slow_statement; + size_t func_in_plugin = (size_t)trampoline_dummy_func_for_mem; + if (func_in_mysqld < INT_MAX && func_in_plugin > INT_MAX) + { + // When the distance from a hot patch function to trampoline_mem is within 2GB, + // the minimum size of hot patching is reduced from 14 to 6. + mmap_flags |= MAP_32BIT; + use_static_memory = false; + } +#endif + + if (use_static_memory) { // use static executable memory we alocated via trampoline_dummy_func_for_mem DATATYPE_ADDRESS addrs = (DATATYPE_ADDRESS)trampoline_dummy_func_for_mem + (page_size - 1); @@ -1625,7 +1641,7 @@ static int audit_plugin_init(void *p) } else // big pages for some reason. allocate mem using mmap { - trampoline_mem = mmap(NULL, page_size, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + trampoline_mem = mmap(NULL, page_size, PROT_READ|PROT_EXEC, mmap_flags, -1, 0); if (MAP_FAILED == trampoline_mem) { sql_print_error("%s unable to mmap memory size: %lu, errno: %d. Aborting.", diff --git a/src/hot_patch.cc b/src/hot_patch.cc index bd89689..046954a 100644 --- a/src/hot_patch.cc +++ b/src/hot_patch.cc @@ -145,19 +145,6 @@ static DATATYPE_ADDRESS get_page_address(void *pointer) return (longp & pageMask); } -// -// This function retrieves the necessary size for the jump -// - -unsigned int jump_size() -{ -#ifndef __x86_64__ - return 5; -#else - return 14; -#endif -} - // // This function writes unconditional jumps // both for x86 and x64 @@ -189,11 +176,48 @@ static void WriteJump(void *pAddress, ULONG_PTR JumpTo) protect((void*)AddressPage, PAGE_SIZE); } +#ifndef __x86_64__ + +#define JUMP_SIZE 5 + +#else + +#define JUMP_SIZE 14 // jump size of WriteJump() +#define JUMP32_SIZE 5 // jump size of WriteJump32() + +static bool CanUseJump32(void *pAddress, ULONG_PTR JumpTo) +{ + int64_t diff = JumpTo - ((ULONG_PTR)pAddress + JUMP32_SIZE); + if (INT32_MIN <= diff && diff <= INT32_MAX) + { + return true; + } + else + { + return false; + } +} + +static void WriteJump32(void *pAddress, ULONG_PTR JumpTo) +{ + int64_t diff = JumpTo - ((ULONG_PTR)pAddress + JUMP32_SIZE); + DATATYPE_ADDRESS AddressPage = get_page_address(pAddress); + unprotect((void*)AddressPage, PAGE_SIZE); + + BYTE *pCur = (BYTE *) pAddress; + *pCur++ = 0xE9; // jmp +imm32 + *(DWORD *)pCur = (DWORD)diff; + + protect((void*)AddressPage, PAGE_SIZE); +} + +#endif + // // Hooks a function // static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_PTR trampolineFunction, - unsigned int *trampolinesize) + unsigned int *trampolinesize, unsigned int *usedsize) { #define MAX_INSTRUCTIONS 100 uint8_t raw[MAX_INSTRUCTIONS]; @@ -203,6 +227,18 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_ #define ASM_MODE 32 #else #define ASM_MODE 64 + enum { + // overwrite 14 bytes in targetFunction. + // jump to newFunction by WriteJump(). + Jump64, + // overwrite 5 bytes in targetFunction. + // jump to newFunction by WriteJump32(). + Jump32, + // overwrite 5 bytes in targetFunction. + // jump to a region in trampolineFunction by WriteJump32() + // and then jump to newFunction by WriteJump(). + IndirectJump, + } jumpType = Jump64; #endif memcpy(raw, (void*)targetFunction, MAX_INSTRUCTIONS); ud_t ud_obj; @@ -250,11 +286,28 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_ uCurrentSize += ud_insn_len (&ud_obj); InstrSize += ud_insn_len (&ud_obj); - if (InstrSize >= jump_size()) // we have enough space so break + if (InstrSize >= JUMP_SIZE) // we have enough space so break { disassemble_valid = true; break; } +#ifdef __x86_64__ + if (InstrSize >= JUMP32_SIZE) + { + if (CanUseJump32((void *)targetFunction, newFunction)) + { + disassemble_valid = true; + jumpType = Jump32; + break; + } + if (CanUseJump32((void *)targetFunction, trampolineFunction + uCurrentSize + JUMP_SIZE)) + { + disassemble_valid = true; + jumpType = IndirectJump; + break; + } + } +#endif } if (protect((void*)trampolineFunctionPage, PAGE_SIZE)) // 0 valid return @@ -271,7 +324,26 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_ } WriteJump((BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize); + *usedsize = uCurrentSize + JUMP_SIZE; +#ifndef __x86_64__ WriteJump((void *) targetFunction, newFunction); +#else + switch (jumpType) { + ULONG_PTR addr; + case Jump64: + WriteJump((void *)targetFunction, newFunction); + break; + case Jump32: + WriteJump32((void *)targetFunction, newFunction); + break; + case IndirectJump: + addr = trampolineFunction + uCurrentSize + JUMP_SIZE; + WriteJump32((void *)targetFunction, addr); + WriteJump((void*)addr, newFunction); + *usedsize += JUMP_SIZE; + break; + } +#endif *trampolinesize = uCurrentSize; return true; } @@ -312,12 +384,12 @@ static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, uns * @Return 0 on success otherwise failure * @See MS Detours paper: http:// research.microsoft.com/pubs/68568/huntusenixnt99.pdf for some background info. */ -int hot_patch_function(void *targetFunction, void *newFunction, void *trampolineFunction, unsigned int *trampolinesize, bool info_print) +int hot_patch_function(void *targetFunction, void *newFunction, void *trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool info_print) { DATATYPE_ADDRESS trampolinePage = get_page_address(trampolineFunction); cond_info_print(info_print, "%s hot patching function: %p, trampolineFunction: %p trampolinePage: %p",log_prefix, (void *)targetFunction, (void *)trampolineFunction, (void *)trampolinePage); if (HookFunction((ULONG_PTR) targetFunction, (ULONG_PTR) newFunction, - (ULONG_PTR) trampolineFunction, trampolinesize)) + (ULONG_PTR) trampolineFunction, trampolinesize, usedsize)) { return 0; }