| Index: third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.cc
|
| diff --git a/third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.cc b/third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.cc
|
| index 4eb391d72fa3d1cf6f672b0e98d869682ca63c30..b0dc393d975a0886447a7d74fb148ce09402dfa3 100644
|
| --- a/third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.cc
|
| +++ b/third_party/tcmalloc/chromium/src/windows/preamble_patcher_with_stub.cc
|
| @@ -29,6 +29,7 @@
|
| *
|
| * ---
|
| * Author: Joi Sigurdsson
|
| + * Author: Scott Francis
|
| *
|
| * Implementation of PreamblePatcher
|
| */
|
| @@ -40,12 +41,20 @@
|
| // Definitions of assembly statements we need
|
| #define ASM_JMP32REL 0xE9
|
| #define ASM_INT3 0xCC
|
| +#define ASM_NOP 0x90
|
| +// X64 opcodes
|
| +#define ASM_MOVRAX_IMM 0xB8
|
| +#define ASM_REXW 0x48
|
| +#define ASM_JMP 0xFF
|
| +#define ASM_JMP_RAX 0xE0
|
| +#define ASM_PUSH 0x68
|
| +#define ASM_RET 0xC3
|
|
|
| namespace sidestep {
|
|
|
| SideStepError PreamblePatcher::RawPatchWithStub(
|
| void* target_function,
|
| - void *replacement_function,
|
| + void* replacement_function,
|
| unsigned char* preamble_stub,
|
| unsigned long stub_size,
|
| unsigned long* bytes_needed) {
|
| @@ -75,23 +84,52 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| // doing it atomically does not help if one of the other threads happens
|
| // to have its eip in the middle of the bytes you change while you change
|
| // them.
|
| + unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
|
| + unsigned int required_trampoline_bytes = 0;
|
| + const unsigned int kRequiredStubJumpBytes = 5;
|
| + const unsigned int kRequiredTargetPatchBytes = 5;
|
|
|
| - // First, deal with a special case that we see with functions that
|
| - // point into an IAT table (including functions linked statically
|
| - // into the application): these function already starts with
|
| - // ASM_JMP32REL. For instance, malloc() might be implemented as a
|
| - // JMP to __malloc(). In that case, we replace the destination of
|
| - // the JMP (__malloc), rather than the JMP itself (malloc). This
|
| - // way we get the correct behavior no matter how malloc gets called.
|
| - void *new_target = ResolveTarget(target_function);
|
| - if (new_target != target_function) { // we're in the IAT case
|
| - // I'd like to just say "target = new_target", but I can't,
|
| - // because the new target will need to have its protections set.
|
| - return RawPatchWithStubAndProtections(new_target, replacement_function,
|
| - preamble_stub, stub_size,
|
| - bytes_needed);
|
| + // Initialize the stub with INT3's just in case.
|
| + if (stub_size) {
|
| + memset(preamble_stub, 0xcc, stub_size);
|
| + }
|
| + if (kIs64BitBinary) {
|
| + // In 64-bit mode JMP instructions are always relative to RIP. If the
|
| + // replacement - target offset is > 2GB, we can't JMP to the replacement
|
| + // function. In this case, we're going to use a trampoline - that is,
|
| + // we're going to do a relative jump to a small chunk of code in the stub
|
| + // that will then do the absolute jump to the replacement function. By
|
| + // doing this, we only need to patch 5 bytes in the target function, as
|
| + // opposed to patching 12 bytes if we were to do an absolute jump.
|
| + //
|
| + // Note that the first byte of the trampoline is a NOP instruction. This
|
| + // is used as a trampoline signature that will be detected when unpatching
|
| + // the function.
|
| + //
|
| + // jmp <trampoline>
|
| + //
|
| + // trampoline:
|
| + // nop
|
| + // mov rax, <replacement_function>
|
| + // jmp rax
|
| + //
|
| + __int64 replacement_target_offset = reinterpret_cast<__int64>(
|
| + replacement_function) - reinterpret_cast<__int64>(target) - 5;
|
| + if (replacement_target_offset > INT_MAX
|
| + || replacement_target_offset < INT_MIN) {
|
| + // The stub needs to be within 2GB of the target for the trampoline to
|
| + // work!
|
| + __int64 trampoline_offset = reinterpret_cast<__int64>(preamble_stub)
|
| + - reinterpret_cast<__int64>(target) - 5;
|
| + if (trampoline_offset > INT_MAX || trampoline_offset < INT_MIN) {
|
| + // We're screwed.
|
| + SIDESTEP_ASSERT(false
|
| + && "Preamble stub is too far from target to patch.");
|
| + return SIDESTEP_UNEXPECTED;
|
| + }
|
| + required_trampoline_bytes = 13;
|
| + }
|
| }
|
| - unsigned char* target = reinterpret_cast<unsigned char*>(new_target);
|
|
|
| // Let's disassemble the preamble of the target function to see if we can
|
| // patch, and to see how much of the preamble we need to take. We need 5
|
| @@ -99,42 +137,76 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| // instructions to get 5 bytes.
|
| MiniDisassembler disassembler;
|
| unsigned int preamble_bytes = 0;
|
| - while (preamble_bytes < 5) {
|
| + unsigned int stub_bytes = 0;
|
| + while (preamble_bytes < kRequiredTargetPatchBytes) {
|
| + unsigned int cur_bytes = 0;
|
| InstructionType instruction_type =
|
| - disassembler.Disassemble(target + preamble_bytes, preamble_bytes);
|
| + disassembler.Disassemble(target + preamble_bytes, cur_bytes);
|
| if (IT_JUMP == instruction_type) {
|
| - SIDESTEP_ASSERT(false &&
|
| - "Unable to patch because there is a jump instruction "
|
| - "in the first 5 bytes.");
|
| - return SIDESTEP_JUMP_INSTRUCTION;
|
| + unsigned int jump_bytes = 0;
|
| + SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION;
|
| + if (IsShortConditionalJump(target + preamble_bytes, cur_bytes)) {
|
| + jump_ret = PatchShortConditionalJump(target + preamble_bytes, cur_bytes,
|
| + preamble_stub + stub_bytes,
|
| + &jump_bytes,
|
| + stub_size - stub_bytes);
|
| + } else if (IsNearConditionalJump(target + preamble_bytes, cur_bytes) ||
|
| + IsNearRelativeJump(target + preamble_bytes, cur_bytes) ||
|
| + IsNearAbsoluteCall(target + preamble_bytes, cur_bytes) ||
|
| + IsNearRelativeCall(target + preamble_bytes, cur_bytes)) {
|
| + jump_ret = PatchNearJumpOrCall(target + preamble_bytes, cur_bytes,
|
| + preamble_stub + stub_bytes, &jump_bytes,
|
| + stub_size - stub_bytes);
|
| + }
|
| + if (jump_ret != SIDESTEP_SUCCESS) {
|
| + SIDESTEP_ASSERT(false &&
|
| + "Unable to patch because there is an unhandled branch "
|
| + "instruction in the initial preamble bytes.");
|
| + return SIDESTEP_JUMP_INSTRUCTION;
|
| + }
|
| + stub_bytes += jump_bytes;
|
| } else if (IT_RETURN == instruction_type) {
|
| SIDESTEP_ASSERT(false &&
|
| "Unable to patch because function is too short");
|
| return SIDESTEP_FUNCTION_TOO_SMALL;
|
| - } else if (IT_GENERIC != instruction_type) {
|
| + } else if (IT_GENERIC == instruction_type) {
|
| + if (IsMovWithDisplacement(target + preamble_bytes, cur_bytes)) {
|
| + unsigned int mov_bytes = 0;
|
| + if (PatchMovWithDisplacement(target + preamble_bytes, cur_bytes,
|
| + preamble_stub + stub_bytes, &mov_bytes,
|
| + stub_size - stub_bytes)
|
| + != SIDESTEP_SUCCESS) {
|
| + return SIDESTEP_UNSUPPORTED_INSTRUCTION;
|
| + }
|
| + stub_bytes += mov_bytes;
|
| + } else {
|
| + memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes),
|
| + reinterpret_cast<void*>(target + preamble_bytes), cur_bytes);
|
| + stub_bytes += cur_bytes;
|
| + }
|
| + } else {
|
| SIDESTEP_ASSERT(false &&
|
| "Disassembler encountered unsupported instruction "
|
| "(either unused or unknown");
|
| return SIDESTEP_UNSUPPORTED_INSTRUCTION;
|
| }
|
| + preamble_bytes += cur_bytes;
|
| }
|
|
|
| if (NULL != bytes_needed)
|
| - *bytes_needed = preamble_bytes + 5;
|
| + *bytes_needed = stub_bytes + kRequiredStubJumpBytes
|
| + + required_trampoline_bytes;
|
|
|
| // Inv: cbPreamble is the number of bytes (at least 5) that we need to take
|
| // from the preamble to have whole instructions that are 5 bytes or more
|
| - // in size total. The size of the stub required is cbPreamble + size of
|
| - // jmp (5)
|
| - if (preamble_bytes + 5 > stub_size) {
|
| + // in size total. The size of the stub required is cbPreamble +
|
| + // kRequiredStubJumpBytes (5) + required_trampoline_bytes (0 or 13)
|
| + if (stub_bytes + kRequiredStubJumpBytes + required_trampoline_bytes
|
| + > stub_size) {
|
| SIDESTEP_ASSERT(false);
|
| return SIDESTEP_INSUFFICIENT_BUFFER;
|
| }
|
|
|
| - // First, copy the preamble that we will overwrite.
|
| - memcpy(reinterpret_cast<void*>(preamble_stub),
|
| - reinterpret_cast<void*>(target), preamble_bytes);
|
| -
|
| // Now, make a jmp instruction to the rest of the target function (minus the
|
| // preamble bytes we moved into the stub) and copy it into our preamble-stub.
|
| // find address to jump to, relative to next address after jmp instruction
|
| @@ -144,16 +216,32 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| #endif
|
| int relative_offset_to_target_rest
|
| = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
|
| - (preamble_stub + preamble_bytes + 5));
|
| + (preamble_stub + stub_bytes + kRequiredStubJumpBytes));
|
| #ifdef _MSC_VER
|
| #pragma warning(pop)
|
| #endif
|
| // jmp (Jump near, relative, displacement relative to next instruction)
|
| - preamble_stub[preamble_bytes] = ASM_JMP32REL;
|
| + preamble_stub[stub_bytes] = ASM_JMP32REL;
|
| // copy the address
|
| - memcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1),
|
| + memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes + 1),
|
| reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
|
|
|
| + if (kIs64BitBinary && required_trampoline_bytes != 0) {
|
| + // Construct the trampoline
|
| + unsigned int trampoline_pos = stub_bytes + kRequiredStubJumpBytes;
|
| + preamble_stub[trampoline_pos] = ASM_NOP;
|
| + preamble_stub[trampoline_pos + 1] = ASM_REXW;
|
| + preamble_stub[trampoline_pos + 2] = ASM_MOVRAX_IMM;
|
| + memcpy(reinterpret_cast<void*>(preamble_stub + trampoline_pos + 3),
|
| + reinterpret_cast<void*>(&replacement_function),
|
| + sizeof(void *));
|
| + preamble_stub[trampoline_pos + 11] = ASM_JMP;
|
| + preamble_stub[trampoline_pos + 12] = ASM_JMP_RAX;
|
| +
|
| + // Now update replacement_function to point to the trampoline
|
| + replacement_function = preamble_stub + trampoline_pos;
|
| + }
|
| +
|
| // Inv: preamble_stub points to assembly code that will execute the
|
| // original function by first executing the first cbPreamble bytes of the
|
| // preamble, then jumping to the rest of the function.
|
| @@ -177,6 +265,7 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| // complete the jmp instruction
|
| memcpy(reinterpret_cast<void*>(target + 1),
|
| reinterpret_cast<void*>(&offset_to_replacement_function), 4);
|
| +
|
| // Set any remaining bytes that were moved to the preamble-stub to INT3 so
|
| // as not to cause confusion (otherwise you might see some strange
|
| // instructions if you look at the disassembly, or even invalid
|
| @@ -184,8 +273,9 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| // some code calls into this portion of the code. If this happens, it
|
| // means that this function cannot be patched using this patcher without
|
| // further thought.
|
| - if (preamble_bytes > 5) {
|
| - memset(reinterpret_cast<void*>(target + 5), ASM_INT3, preamble_bytes - 5);
|
| + if (preamble_bytes > kRequiredTargetPatchBytes) {
|
| + memset(reinterpret_cast<void*>(target + kRequiredTargetPatchBytes),
|
| + ASM_INT3, preamble_bytes - kRequiredTargetPatchBytes);
|
| }
|
|
|
| // Inv: The memory pointed to by target_function now points to a relative
|
| @@ -193,7 +283,13 @@ SideStepError PreamblePatcher::RawPatchWithStub(
|
| // stub contains the first stub_size bytes of the original target
|
| // function's preamble code, followed by a relative jump back to the next
|
| // instruction after the first cbPreamble bytes.
|
| -
|
| + //
|
| + // In 64-bit mode the memory pointed to by target_function *may* point to a
|
| + // relative jump instruction that jumps to a trampoline which will then
|
| + // perform an absolute jump to the replacement function. The preamble stub
|
| + // still contains the original target function's preamble code, followed by a
|
| + // jump back to the instructions after the first preamble bytes.
|
| + //
|
| return SIDESTEP_SUCCESS;
|
| }
|
|
|
|
|