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.
pull/142/head
Kubo Takehiro 2016-06-13 19:49:05 +09:00
parent 4c8f9694a2
commit 1cc4406358
3 changed files with 112 additions and 26 deletions

View File

@ -14,9 +14,7 @@
#define GETPAGESIZE() sysconf (_SC_PAGE_SIZE) #define GETPAGESIZE() sysconf (_SC_PAGE_SIZE)
unsigned int jump_size(); int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool log_info);
int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, bool log_info);
void remove_hot_patch_function(void* targetFunction, void* trampolineFunction, unsigned int trampolinesize, bool log_info); void remove_hot_patch_function(void* targetFunction, void* trampolineFunction, unsigned int trampolinesize, bool log_info);

View File

@ -1317,8 +1317,9 @@ static int do_hot_patch(void ** trampoline_func_pp, unsigned int * trampoline_si
*trampoline_func_pp = (void*)(addrs & ~0x0F); *trampoline_func_pp = (void*)(addrs & ~0x0F);
// hot patch functions // hot patch functions
unsigned int used_size;
int res = hot_patch_function(target_function, audit_function, 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) if (res != 0)
{ {
// hot patch failed. // hot patch failed.
@ -1327,9 +1328,9 @@ static int do_hot_patch(void ** trampoline_func_pp, unsigned int * trampoline_si
return 1; return 1;
} }
sql_print_information( sql_print_information(
"%s hot patch for: %s (%p) complete. Audit func: %p, Trampoline address: %p size: %u.", "%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); log_prefix, func_name, target_function, audit_function, *trampoline_func_pp, *trampoline_size, used_size);
trampoline_mem_free = (void *)(((DATATYPE_ADDRESS)*trampoline_func_pp) + *trampoline_size + jump_size()); trampoline_mem_free = (void *)(((DATATYPE_ADDRESS)*trampoline_func_pp) + used_size);
return 0; return 0;
} }
@ -1614,7 +1615,22 @@ static int audit_plugin_init(void *p)
// align our trampoline mem on its own page // align our trampoline mem on its own page
const unsigned long page_size = GETPAGESIZE(); const unsigned long page_size = GETPAGESIZE();
const unsigned long std_page_size = 4096; 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 // 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); 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 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) if (MAP_FAILED == trampoline_mem)
{ {
sql_print_error("%s unable to mmap memory size: %lu, errno: %d. Aborting.", sql_print_error("%s unable to mmap memory size: %lu, errno: %d. Aborting.",

View File

@ -145,19 +145,6 @@ static DATATYPE_ADDRESS get_page_address(void *pointer)
return (longp & pageMask); 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 // This function writes unconditional jumps
// both for x86 and x64 // both for x86 and x64
@ -189,11 +176,48 @@ static void WriteJump(void *pAddress, ULONG_PTR JumpTo)
protect((void*)AddressPage, PAGE_SIZE); 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 // Hooks a function
// //
static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_PTR trampolineFunction, 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 #define MAX_INSTRUCTIONS 100
uint8_t raw[MAX_INSTRUCTIONS]; uint8_t raw[MAX_INSTRUCTIONS];
@ -203,6 +227,18 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
#define ASM_MODE 32 #define ASM_MODE 32
#else #else
#define ASM_MODE 64 #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 #endif
memcpy(raw, (void*)targetFunction, MAX_INSTRUCTIONS); memcpy(raw, (void*)targetFunction, MAX_INSTRUCTIONS);
ud_t ud_obj; ud_t ud_obj;
@ -250,11 +286,28 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
uCurrentSize += ud_insn_len (&ud_obj); uCurrentSize += ud_insn_len (&ud_obj);
InstrSize += 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; disassemble_valid = true;
break; 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 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); WriteJump((BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize);
*usedsize = uCurrentSize + JUMP_SIZE;
#ifndef __x86_64__
WriteJump((void *) targetFunction, newFunction); 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; *trampolinesize = uCurrentSize;
return true; return true;
} }
@ -312,12 +384,12 @@ static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, uns
* @Return 0 on success otherwise failure * @Return 0 on success otherwise failure
* @See MS Detours paper: http:// research.microsoft.com/pubs/68568/huntusenixnt99.pdf for some background info. * @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); 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); 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, if (HookFunction((ULONG_PTR) targetFunction, (ULONG_PTR) newFunction,
(ULONG_PTR) trampolineFunction, trampolinesize)) (ULONG_PTR) trampolineFunction, trampolinesize, usedsize))
{ {
return 0; return 0;
} }