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..8017037 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,57 @@ 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 JUMP_SIZE2 6 // jump size of WriteJump2() +#define ADDR_SIZE 8 + +static bool CanUseWriteJump2(void *pAddress, ULONG_PTR AddrPos) +{ + int64_t diff = AddrPos - ((ULONG_PTR)pAddress + JUMP_SIZE2); + if (INT32_MIN <= diff && diff <= INT32_MAX) + { + return true; + } + else + { + return false; + } +} + +static void WriteJump2(void *pAddress, ULONG_PTR AddrPos, ULONG_PTR JumpTo) +{ + DATATYPE_ADDRESS AddressPage = get_page_address(pAddress); + unprotect((void*)AddressPage, PAGE_SIZE); + + BYTE *pCur = (BYTE *) pAddress; + *pCur = 0xff; // jmp [rip+addr] + *(++pCur) = 0x25; + int64_t diff = AddrPos - ((ULONG_PTR)pAddress + JUMP_SIZE2); + *((DWORD *) ++pCur) = (DWORD)diff; + + protect((void*)AddressPage, PAGE_SIZE); + + DATATYPE_ADDRESS AddrPosPage = get_page_address((void*)AddrPos); + unprotect((void*)AddrPosPage, PAGE_SIZE); + + *((ULONG_PTR *)AddrPos) = JumpTo; + + protect((void*)AddrPosPage, 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 +236,7 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_ #define ASM_MODE 32 #else #define ASM_MODE 64 + bool useWriteJump2 = false; #endif memcpy(raw, (void*)targetFunction, MAX_INSTRUCTIONS); ud_t ud_obj; @@ -250,11 +284,20 @@ 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 >= JUMP_SIZE2 && + CanUseWriteJump2((void *)targetFunction, trampolineFunction + uCurrentSize + JUMP_SIZE)) + { + disassemble_valid = true; + useWriteJump2 = true; + break; + } +#endif } if (protect((void*)trampolineFunctionPage, PAGE_SIZE)) // 0 valid return @@ -271,7 +314,20 @@ 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 + if (useWriteJump2) + { + WriteJump2((void *)targetFunction, trampolineFunction + uCurrentSize + JUMP_SIZE, newFunction); + *usedsize += ADDR_SIZE; + } + else + { + WriteJump((void *)targetFunction, newFunction); + } +#endif *trampolinesize = uCurrentSize; return true; } @@ -312,12 +368,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; }