[compiler-rt] r275123 - [compiler-rt] Refactor the interception code on windows.

Etienne Bergeron via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 11 16:02:18 PDT 2016


Author: etienneb
Date: Mon Jul 11 18:02:18 2016
New Revision: 275123

URL: http://llvm.org/viewvc/llvm-project?rev=275123&view=rev
Log:
[compiler-rt] Refactor the interception code on windows.

Summary:
This is a cleanup and refactoring of the interception code on windows

Enhancement:
  * Adding the support for 64-bits code
  * Adding several hooking technique:
    * Detour
    * JumpRedirect
    * HotPatch
    * Trampoline
  * Adding a trampoline memory pool (64-bits) and release the allocated memory in unittests

Cleanup:
  * Adding unittests for 64-bits hooking techniques
  * Enhancing the RoundUpInstruction by sharing common decoder

Reviewers: rnk

Subscribers: llvm-commits, wang0109, chrisha

Differential Revision: http://reviews.llvm.org/D22111

Modified:
    compiler-rt/trunk/lib/interception/interception_win.cc
    compiler-rt/trunk/lib/interception/interception_win.h
    compiler-rt/trunk/lib/interception/tests/interception_win_test.cc

Modified: compiler-rt/trunk/lib/interception/interception_win.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/interception_win.cc?rev=275123&r1=275122&r2=275123&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception_win.cc (original)
+++ compiler-rt/trunk/lib/interception/interception_win.cc Mon Jul 11 18:02:18 2016
@@ -10,16 +10,160 @@
 // This file is a part of AddressSanitizer, an address sanity checker.
 //
 // Windows-specific interception methods.
+//
+// This file is implementing several hooking techniques to intercept calls
+// to functions. The hooks are dynamically installed by modifying the assembly
+// code.
+//
+// The hooking techniques are making assumptions on the way the code is
+// generated and are safe under these assumptions.
+//
+// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow
+// arbitrary branching on the whole memory space, the notion of trampoline
+// region is used. A trampoline region is a memory space withing 2G boundary
+// where it is safe to add custom assembly code to build 64-bit jumps.
+//
+// Hooking techniques
+// ==================
+//
+// 1) Detour
+//
+//    The Detour hooking technique is assuming the presence of an header with
+//    padding and an overridable 2-bytes nop instruction (mov edi, edi). The
+//    nop instruction can safely be replaced by a 2-bytes jump without any need
+//    to save the instruction. A jump to the target is encoded in the function
+//    header and the nop instruction is replaced by a short jump to the header.
+//
+//        head:  5 x nop                 head:  jmp <hook>
+//        func:  mov edi, edi    -->     func:  jmp short <head>
+//               [...]                   real:  [...]
+//
+//    This technique is only implemented on 32-bit architecture.
+//    Most of the time, Windows API are hookable with the detour technique.
+//
+// 2) Redirect Jump
+//
+//    The redirect jump is applicable when the first instruction is a direct
+//    jump. The instruction is replaced by jump to the hook.
+//
+//        func:  jmp <label>     -->     func:  jmp <hook>
+//
+//    On an 64-bit architecture, a trampoline is inserted.
+//
+//        func:  jmp <label>     -->     func:  jmp <tramp>
+//                                              [...]
+//
+//                                   [trampoline]
+//                                      tramp:  jmp QWORD [addr]
+//                                       addr:  .bytes <hook>
+//
+//    Note: <real> is equilavent to <label>.
+//
+// 3) HotPatch
+//
+//    The HotPatch hooking is assuming the presence of an header with padding
+//    and a first instruction with at least 2-bytes.
+//
+//    The reason to enforce the 2-bytes limitation is to provide the minimal
+//    space to encode a short jump. HotPatch technique is only rewriting one
+//    instruction to avoid breaking a sequence of instructions containing a
+//    branching target.
+//
+//    Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag.
+//      see: https://msdn.microsoft.com/en-us/library/ms173507.aspx
+//    Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits.
+//
+//        head:   5 x nop                head:  jmp <hook>
+//        func:   <instr>        -->     func:  jmp short <head>
+//                [...]                  body:  [...]
+//
+//                                   [trampoline]
+//                                       real:  <instr>
+//                                              jmp <body>
+//
+//    On an 64-bit architecture:
+//
+//        head:   6 x nop                head:  jmp QWORD [addr1]
+//        func:   <instr>        -->     func:  jmp short <head>
+//                [...]                  body:  [...]
+//
+//                                   [trampoline]
+//                                      addr1:  .bytes <hook>
+//                                       real:  <instr>
+//                                              jmp QWORD [addr2]
+//                                      addr2:  .bytes <body>
+//
+// 4) Trampoline
+//
+//    The Trampoline hooking technique is the most aggressive one. It is
+//    assuming that there is a sequence of instructions that can be safely
+//    replaced by a jump (enough room and no incoming branches).
+//
+//    Unfortunately, these assumptions can't be safely presumed and code may
+//    be broken after hooking.
+//
+//        func:   <instr>        -->     func:  jmp <hook>
+//                <instr>
+//                [...]                  body:  [...]
+//
+//                                   [trampoline]
+//                                       real:  <instr>
+//                                              <instr>
+//                                              jmp <body>
+//
+//    On an 64-bit architecture:
+//
+//        func:   <instr>        -->     func:  jmp QWORD [addr1]
+//                <instr>
+//                [...]                  body:  [...]
+//
+//                                   [trampoline]
+//                                      addr1:  .bytes <hook>
+//                                       real:  <instr>
+//                                              <instr>
+//                                              jmp QWORD [addr2]
+//                                      addr2:  .bytes <body>
 //===----------------------------------------------------------------------===//
 
 #ifdef _WIN32
 
 #include "interception.h"
+#include "sanitizer_common/sanitizer_platform.h"
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace __interception {
 
+static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
+static const int kJumpInstructionLength = 5;
+static const int kShortJumpInstructionLength = 2;
+static const int kIndirectJumpInstructionLength = 6;
+static const int kBranchLength =
+    FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength);
+static const int kDirectBranchLength = kBranchLength + kAddressLength;
+
+static void InterceptionFailed() {
+  // Do we have a good way to abort with an error message here?
+  __debugbreak();
+}
+
+static bool DistanceIsWithin2Gig(uptr from, uptr target) {
+  if (from < target)
+    return target - from <= (uptr)0x7FFFFFFFU;
+  else
+    return from - target <= (uptr)0x80000000U;
+}
+
+static uptr GetMmapGranularity() {
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwAllocationGranularity;
+}
+
+static uptr RoundUpTo(uptr size, uptr boundary) {
+  return (size + boundary - 1) & ~(boundary - 1);
+}
+
 // FIXME: internal_str* and internal_mem* functions should be moved from the
 // ASan sources into interception/.
 
@@ -35,334 +179,553 @@ static void _memcpy(void *dst, void *src
     dst_c[i] = src_c[i];
 }
 
+static bool ChangeMemoryProtection(
+    uptr address, uptr size, DWORD *old_protection) {
+  return ::VirtualProtect((void*)address, size,
+                          PAGE_EXECUTE_READWRITE,
+                          old_protection) != FALSE;
+}
+
+static bool RestoreMemoryProtection(
+    uptr address, uptr size, DWORD old_protection) {
+  DWORD unused;
+  return ::VirtualProtect((void*)address, size,
+                          old_protection,
+                          &unused) != FALSE;
+}
+
+static bool IsMemoryPadding(uptr address, uptr size) {
+  u8* function = (u8*)address;
+  for (size_t i = 0; i < size; ++i)
+    if (function[i] != 0x90 && function[i] != 0xCC)
+      return false;
+  return true;
+}
+
+static void WritePadding(uptr from, uptr size) {
+  _memset((void*)from, 0xCC, (size_t)size);
+}
+
+static void CopyInstructions(uptr from, uptr to, uptr size) {
+  _memcpy((void*)from, (void*)to, (size_t)size);
+}
+
+static void WriteJumpInstruction(uptr from, uptr target) {
+  if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target))
+    InterceptionFailed();
+  ptrdiff_t offset = target - from - kJumpInstructionLength;
+  *(u8*)from = 0xE9;
+  *(u32*)(from + 1) = offset;
+}
+
+static void WriteShortJumpInstruction(uptr from, uptr target) {
+  sptr offset = target - from - kShortJumpInstructionLength;
+  if (offset < -128 || offset > 127)
+    InterceptionFailed();
+  *(u8*)from = 0xEB;
+  *(u8*)(from + 1) = (u8)offset;
+}
+
 #if SANITIZER_WINDOWS64
-static void WriteIndirectJumpInstruction(char *jmp_from, uptr *indirect_target) {  // NOLINT
-  // jmp [rip + XXYYZZWW] = FF 25 WW ZZ YY XX, where
-  // XXYYZZWW is an offset from jmp_from.
-  // The displacement is still 32-bit in x64, so indirect_target must be located
-  // within +/- 2GB range.
-  int offset = (int)(indirect_target - (uptr *)jmp_from);
-  jmp_from[0] = '\xFF';
-  jmp_from[1] = '\x25';
-  *(int*)(jmp_from + 2) = offset;
+static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) {
+  // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative
+  // offset.
+  // The offset is the distance from then end of the jump instruction to the
+  // memory location containing the targeted address. The displacement is still
+  // 32-bit in x64, so indirect_target must be located within +/- 2GB range.
+  int offset = indirect_target - from - kIndirectJumpInstructionLength;
+  if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength,
+                            indirect_target)) {
+    InterceptionFailed();
+  }
+  *(u16*)from = 0x25FF;
+  *(u32*)(from + 2) = offset;
 }
+#endif
+
+static void WriteBranch(
+    uptr from, uptr indirect_target, uptr target) {
+#if SANITIZER_WINDOWS64
+  WriteIndirectJumpInstruction(from, indirect_target);
+  *(u64*)indirect_target = target;
 #else
-static void WriteJumpInstruction(char *jmp_from, char *to) {
-  // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset from jmp_from
-  // to the next instruction to the destination.
-  ptrdiff_t offset = to - jmp_from - 5;
-  *jmp_from = '\xE9';
-  *(ptrdiff_t*)(jmp_from + 1) = offset;
-}
+  (void)indirect_target;
+  WriteJumpInstruction(from, target);
 #endif
+}
 
-static void WriteTrampolineJumpInstruction(char *jmp_from, char *to) {
+static void WriteDirectBranch(uptr from, uptr target) {
 #if SANITIZER_WINDOWS64
   // Emit an indirect jump through immediately following bytes:
-  // jmp_from:
-  //   jmp [rip + 6]
-  //   .quad to
-  // Store the address.
-  uptr *indirect_target = (uptr *)(jmp_from + 6);
-  *indirect_target = (uptr)to;
-  // Write the indirect jump.
-  WriteIndirectJumpInstruction(jmp_from, indirect_target);
+  //   jmp [rip + kBranchLength]
+  //   .quad <target>
+  WriteBranch(from, from + kBranchLength, target);
 #else
-  WriteJumpInstruction(jmp_from, to);
+  WriteJumpInstruction(from, target);
 #endif
 }
 
-static void WriteInterceptorJumpInstruction(char *jmp_from, char *to) {
+struct TrampolineMemoryRegion {
+  uptr content;
+  uptr allocated_size;
+  uptr max_size;
+};
+
+static const uptr kTrampolineScanLimitRange = 1 << 30;  // 1 gig
+static const int kMaxTrampolineRegion = 1024;
+static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
+
+static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
 #if SANITIZER_WINDOWS64
-  // Emit an indirect jump through immediately following bytes:
-  // jmp_from:
-  //   jmp [rip - 8]
-  //   .quad to
-  // Store the address.
-  uptr *indirect_target = (uptr *)(jmp_from - 8);
-  *indirect_target = (uptr)to;
-  // Write the indirect jump.
-  WriteIndirectJumpInstruction(jmp_from, indirect_target);
+  uptr address = image_address;
+  uptr scanned = 0;
+  while (scanned < kTrampolineScanLimitRange) {
+    MEMORY_BASIC_INFORMATION info;
+    if (!::VirtualQuery((void*)address, &info, sizeof(info)))
+      return nullptr;
+
+    // Check whether a region can be allocated at |address|.
+    if (info.State == MEM_FREE && info.RegionSize >= granularity) {
+      void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
+                                  granularity,
+                                  MEM_RESERVE | MEM_COMMIT,
+                                  PAGE_EXECUTE_READWRITE);
+      return page;
+    }
+
+    // Move to the next region.
+    address = (uptr)info.BaseAddress + info.RegionSize;
+    scanned += info.RegionSize;
+  }
+  return nullptr;
 #else
-  WriteJumpInstruction(jmp_from, to);
+  return ::VirtualAlloc(nullptr,
+                        granularity,
+                        MEM_RESERVE | MEM_COMMIT,
+                        PAGE_EXECUTE_READWRITE);
 #endif
 }
 
-static char *GetMemoryForTrampoline(size_t size) {
-  // Trampolines are allocated from a common pool.
-  const int POOL_SIZE = 1024;
-  static char *pool = NULL;
-  static size_t pool_used = 0;
-  if (!pool) {
-    pool = (char *)VirtualAlloc(NULL, POOL_SIZE, MEM_RESERVE | MEM_COMMIT,
-                                PAGE_EXECUTE_READWRITE);
-    // FIXME: Might want to apply PAGE_EXECUTE_READ access after all the
-    // interceptors are in place.
-    if (!pool)
-      return NULL;
-    _memset(pool, 0xCC /* int 3 */, POOL_SIZE);
-  }
-
-  if (pool_used + size > POOL_SIZE)
-    return NULL;
-
-  char *ret = pool + pool_used;
-  pool_used += size;
-  return ret;
+// Used by unittests to release mapped memory space.
+void TestOnlyReleaseTrampolineRegions() {
+  for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
+    TrampolineMemoryRegion *current = &TrampolineRegions[bucket];
+    if (current->content == 0)
+      return;
+    ::VirtualFree((void*)current->content, 0, MEM_RELEASE);
+    current->content = 0;
+  }
 }
 
-// Returns 0 on error.
-static size_t RoundUpToInstrBoundary(size_t size, char *code) {
+static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
+  // Find a region within 2G with enough space to allocate |size| bytes.
+  TrampolineMemoryRegion *region = nullptr;
+  for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
+    TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
+    if (current->content == 0) {
+      // No valid region found, allocate a new region.
+      size_t bucket_size = GetMmapGranularity();
+      void *content = AllocateTrampolineRegion(image_address, bucket_size);
+      if (content == nullptr)
+        return 0U;
+
+      current->content = (uptr)content;
+      current->allocated_size = 0;
+      current->max_size = bucket_size;
+      region = current;
+      break;
+    } else if (current->max_size - current->allocated_size > size) {
 #if SANITIZER_WINDOWS64
-  // Win64 RoundUpToInstrBoundary is a work in progress.
-  size_t cursor = 0;
-  while (cursor < size) {
-    switch (code[cursor]) {
-      case '\x57':  // 57 : push rdi
-        cursor++;
-        continue;
-      case '\x90':  // 90 : nop
-        cursor++;
-        continue;
-      case '\xb8':  // b8 XX XX XX XX : mov eax, XX XX XX XX
-        cursor += 5;
-        continue;
+        // In 64-bits, the memory space must be allocated within 2G boundary.
+        uptr next_address = current->content + current->allocated_size;
+        if (next_address < image_address ||
+            next_address - image_address >= 0x7FFFF0000)
+          continue;
+#endif
+      // The space can be allocated in the current region.
+      region = current;
+      break;
     }
+  }
 
-    switch (*(u16*)(code + cursor)) {  // NOLINT
-      case 0x5540:  // 40 55 : rex push rbp
-      case 0x5340:  // 40 53 : rex push rbx
-        cursor += 2;
-        continue;
-    }
+  // Failed to find a region.
+  if (region == nullptr)
+    return 0U;
+
+  // Allocate the space in the current region.
+  uptr allocated_space = region->content + region->allocated_size;
+  region->allocated_size += size;
+  WritePadding(allocated_space, size);
 
-    switch (0x00FFFFFF & *(u32*)(code + cursor)) {
-      case 0xc18b48:    // 48 8b c1 : mov rax, rcx
-      case 0xc48b48:    // 48 8b c4 : mov rax, rsp
-      case 0xd9f748:    // 48 f7 d9 : neg rcx
-      case 0xd12b48:    // 48 2b d1 : sub rdx, rcx
-      case 0x07c1f6:    // f6 c1 07 : test cl, 0x7
-      case 0xc0854d:    // 4d 85 c0 : test r8, r8
-      case 0xc2b60f:    // 0f b6 c2 : movzx eax, dl
-      case 0xc03345:    // 45 33 c0 : xor r8d, r8d
-      case 0xd98b4c:    // 4c 8b d9 : mov r11, rcx
-      case 0xd28b4c:    // 4c 8b d2 : mov r10, rdx
-      case 0xd2b60f:    // 0f b6 d2 : movzx edx, dl
-      case 0xca2b48:    // 48 2b ca : sub rcx, rdx
-      case 0x10b70f:    // 0f b7 10 : movzx edx, WORD PTR [rax]
-      case 0xc00b4d:    // 3d 0b c0 : or r8, r8
-      case 0xd18b48:    // 48 8b d1 : mov rdx, rcx
-      case 0xdc8b4c:    // 4c 8b dc : mov r11,rsp
-      case 0xd18b4c:    // 4c 8b d1 : mov r10, rcx
-        cursor += 3;
-        continue;
-
-      case 0xec8348:    // 48 83 ec XX : sub rsp, 0xXX
-      case 0xf88349:    // 49 83 f8 XX : cmp r8, XX
-      case 0x588948:    // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
-        cursor += 4;
-        continue;
-
-      case 0x058b48:    // 48 8b 05 XX XX XX XX
-                        // = mov rax, QWORD PTR [rip+ 0xXXXXXXXX]
-      case 0x25ff48:    // 48 ff 25 XX XX XX XX
-                        // = rex.W jmp QWORD PTR [rip + 0xXXXXXXXX]
-        cursor += 7;
-        continue;
-    }
+  return allocated_space;
+}
 
-    switch (*(u32*)(code + cursor)) {
-      case 0x24448b48:  // 48 8b 44 24 XX : mov rax, qword ptr [rsp + 0xXX]
-        cursor += 5;
-        continue;
-    }
+// Returns 0 on error.
+static size_t GetInstructionSize(uptr address) {
+  switch (*(u8*)address) {
+    case 0x90:  // 90 : nop
+      return 1;
+
+    case 0x50:  // push eax / rax
+    case 0x51:  // push ecx / rcx
+    case 0x52:  // push edx / rdx
+    case 0x53:  // push ebx / rbx
+    case 0x54:  // push esp / rsp
+    case 0x55:  // push ebp / rbp
+    case 0x56:  // push esi / rsi
+    case 0x57:  // push edi / rdi
+    case 0x5D:  // pop ebp / rbp
+      return 1;
+
+    case 0x6A:  // 6A XX = push XX
+      return 2;
+
+    case 0xb8:  // b8 XX XX XX XX : mov eax, XX XX XX XX
+    case 0xB9:  // b9 XX XX XX XX : mov ecx, XX XX XX XX
+    case 0xA1:  // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX]
+      return 5;
+
+    // Cannot overwrite control-instruction. Return 0 to indicate failure.
+    case 0xE9:  // E9 XX XX XX XX : jmp <label>
+    case 0xE8:  // E8 XX XX XX XX : call <func>
+    case 0xC3:  // C3 : ret
+    case 0xEB:  // EB XX : jmp XX (short jump)
+    case 0x70:  // 7Y YY : jy XX (short conditional jump)
+    case 0x71:
+    case 0x72:
+    case 0x73:
+    case 0x74:
+    case 0x75:
+    case 0x76:
+    case 0x77:
+    case 0x78:
+    case 0x79:
+    case 0x7A:
+    case 0x7B:
+    case 0x7C:
+    case 0x7D:
+    case 0x7E:
+    case 0x7F:
+      return 0;
+  }
 
-    // Check first 5 bytes.
-    switch (0xFFFFFFFFFFull & *(u64*)(code + cursor)) {
-      case 0x08245c8948:    // 48 89 5c 24 08 : mov QWORD PTR [rsp+0x8], rbx
-      case 0x1024748948:    // 48 89 74 24 10 : mov QWORD PTR [rsp+0x10], rsi
-        cursor += 5;
-        continue;
-    }
+  switch (*(u16*)(address)) {
+    case 0xFF8B:  // 8B FF : mov edi, edi
+    case 0xEC8B:  // 8B EC : mov ebp, esp
+    case 0xc889:  // 89 C8 : mov eax, ecx
+    case 0xC18B:  // 8B C1 : mov eax, ecx
+    case 0xC033:  // 33 C0 : xor eax, eax
+    case 0xC933:  // 33 C9 : xor ecx, ecx
+    case 0xD233:  // 33 D2 : xor edx, edx
+      return 2;
+
+    // Cannot overwrite control-instruction. Return 0 to indicate failure.
+    case 0x25FF:  // FF 25 XX XX XX XX : jmp [XXXXXXXX]
+      return 0;
+  }
 
-    // Check 8 bytes.
-    switch (*(u64*)(code + cursor)) {
-      case 0x90909090909006EBull:  // JMP +6,  6x NOP
-        cursor += 8;
-        continue;
-    }
+#if SANITIZER_WINDOWS64
+  switch (*(u16*)address) {
+    case 0x5040:  // push rax
+    case 0x5140:  // push rcx
+    case 0x5240:  // push rdx
+    case 0x5340:  // push rbx
+    case 0x5440:  // push rsp
+    case 0x5540:  // push rbp
+    case 0x5640:  // push rsi
+    case 0x5740:  // push rdi
+    case 0x5441:  // push r12
+    case 0x5541:  // push r13
+    case 0x5641:  // push r14
+    case 0x5741:  // push r15
+      return 2;
+  }
 
-    // Unknown instructions!
-    __debugbreak();
+  switch (0x00FFFFFF & *(u32*)address) {
+    case 0xe58948:    // 48 8b c4 : mov rbp, rsp
+    case 0xc18b48:    // 48 8b c1 : mov rax, rcx
+    case 0xc48b48:    // 48 8b c4 : mov rax, rsp
+    case 0xd9f748:    // 48 f7 d9 : neg rcx
+    case 0xd12b48:    // 48 2b d1 : sub rdx, rcx
+    case 0x07c1f6:    // f6 c1 07 : test cl, 0x7
+    case 0xc0854d:    // 4d 85 c0 : test r8, r8
+    case 0xc2b60f:    // 0f b6 c2 : movzx eax, dl
+    case 0xc03345:    // 45 33 c0 : xor r8d, r8d
+    case 0xd98b4c:    // 4c 8b d9 : mov r11, rcx
+    case 0xd28b4c:    // 4c 8b d2 : mov r10, rdx
+    case 0xd2b60f:    // 0f b6 d2 : movzx edx, dl
+    case 0xca2b48:    // 48 2b ca : sub rcx, rdx
+    case 0x10b70f:    // 0f b7 10 : movzx edx, WORD PTR [rax]
+    case 0xc00b4d:    // 3d 0b c0 : or r8, r8
+    case 0xd18b48:    // 48 8b d1 : mov rdx, rcx
+    case 0xdc8b4c:    // 4c 8b dc : mov r11,rsp
+    case 0xd18b4c:    // 4c 8b d1 : mov r10, rcx
+      return 3;
+
+    case 0xec8348:    // 48 83 ec XX : sub rsp, XX
+    case 0xf88349:    // 49 83 f8 XX : cmp r8, XX
+    case 0x588948:    // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
+      return 4;
+
+    case 0x058b48:    // 48 8b 05 XX XX XX XX :
+                      //   mov rax, QWORD PTR [rip + XXXXXXXX]
+    case 0x25ff48:    // 48 ff 25 XX XX XX XX :
+                      //   rex.W jmp QWORD PTR [rip + XXXXXXXX]
+      return 7;
+  }
+
+  switch (*(u32*)(address)) {
+    case 0x24448b48:  // 48 8b 44 24 XX : mov rax, qword ptr [rsp + XX]
+    case 0x245c8948:  // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
+    case 0x24748948:  // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi
+      return 5;
   }
 
-  return cursor;
 #else
+
+  switch (*(u16*)address) {
+    case 0x458B:  // 8B 45 XX : mov eax, dword ptr [ebp + XX]
+    case 0x5D8B:  // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
+    case 0x7D8B:  // 8B 7D XX : mov edi, dword ptr [ebp + XX]
+    case 0xEC83:  // 83 EC XX : sub esp, XX
+    case 0x75FF:  // FF 75 XX : push dword ptr [ebp + XX]
+      return 3;
+    case 0xC1F7:  // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
+    case 0x25FF:  // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
+      return 6;
+    case 0x3D83:  // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
+      return 7;
+    case 0x7D83:  // 83 7D XX YY : cmp dword ptr [ebp + XX], YY
+      return 4;
+  }
+
+  switch (0x00FFFFFF & *(u32*)address) {
+    case 0x24448A:  // 8A 44 24 XX : mov eal, dword ptr [esp + XX]
+    case 0x24448B:  // 8B 44 24 XX : mov eax, dword ptr [esp + XX]
+    case 0x244C8B:  // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX]
+    case 0x24548B:  // 8B 54 24 XX : mov edx, dword ptr [esp + XX]
+    case 0x24748B:  // 8B 74 24 XX : mov esi, dword ptr [esp + XX]
+    case 0x247C8B:  // 8B 7C 24 XX : mov edi, dword ptr [esp + XX]
+      return 4;
+  }
+
+  switch (*(u32*)address) {
+    case 0x2444B60F:  // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX]
+      return 5;
+  }
+#endif
+
+  // Unknown instruction!
+  // FIXME: Unknown instruction failures might happen when we add a new
+  // interceptor or a new compiler version. In either case, they should result
+  // in visible and readable error messages. However, merely calling abort()
+  // leads to an infinite recursion in CheckFailed.
+  InterceptionFailed();
+  return 0;
+}
+
+// Returns 0 on error.
+static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
   size_t cursor = 0;
   while (cursor < size) {
-    switch (code[cursor]) {
-      case '\xE8':  // E8 XX XX XX XX = call <func>
-      case '\xE9':  // E9 XX XX XX XX = jmp <label>
-      case '\xC3':  // C3 = ret
-      case '\xEB':  // EB XX = jmp XX (short jump)
-      case '\x70':  // 7X YY = jx XX (short conditional jump)
-      case '\x71':
-      case '\x72':
-      case '\x73':
-      case '\x74':
-      case '\x75':
-      case '\x76':
-      case '\x77':
-      case '\x78':
-      case '\x79':
-      case '\x7A':
-      case '\x7B':
-      case '\x7C':
-      case '\x7D':
-      case '\x7E':
-      case '\x7F':
-        return 0;
-
-      case '\x50':  // push eax
-      case '\x51':  // push ecx
-      case '\x52':  // push edx
-      case '\x53':  // push ebx
-      case '\x54':  // push esp
-      case '\x55':  // push ebp
-      case '\x56':  // push esi
-      case '\x57':  // push edi
-      case '\x5D':  // pop ebp
-        cursor++;
-        continue;
-      case '\x6A':  // 6A XX = push XX
-        cursor += 2;
-        continue;
-      case '\xB8':  // B8 XX YY ZZ WW = mov eax, WWZZYYXX
-        cursor += 5;
-        continue;
-    }
-    switch (*(u16*)(code + cursor)) {  // NOLINT
-      case 0xFF8B:  // 8B FF = mov edi, edi
-      case 0xEC8B:  // 8B EC = mov ebp, esp
-      case 0xC033:  // 33 C0 = xor eax, eax
-      case 0xC933:  // 33 C9 = xor ecx, ecx
-        cursor += 2;
-        continue;
-      case 0x458B:  // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
-      case 0x5D8B:  // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
-      case 0x7D8B:  // 8B 7D XX = mov edi, dword ptr [ebp+XXh]
-      case 0xEC83:  // 83 EC XX = sub esp, XX
-      case 0x75FF:  // FF 75 XX = push dword ptr [ebp+XXh]
-        cursor += 3;
-        continue;
-      case 0xC1F7:  // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
-      case 0x25FF:  // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX]
-        cursor += 6;
-        continue;
-      case 0x3D83:  // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
-        cursor += 7;
-        continue;
-      case 0x7D83:  // 83 7D XX YY = cmp dword ptr [ebp+XXh], YY
-        cursor += 4;
-        continue;
-    }
-    switch (0x00FFFFFF & *(u32*)(code + cursor)) {
-      case 0x24448A:  // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
-      case 0x24448B:  // 8B 44 24 XX = mov eax, dword ptr [esp+XXh]
-      case 0x244C8B:  // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
-      case 0x24548B:  // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
-      case 0x24748B:  // 8B 74 24 XX = mov esi, dword ptr [esp+XXh]
-      case 0x247C8B:  // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh]
-        cursor += 4;
-        continue;
-    }
-    switch (*(u32*)(code + cursor)) {
-      case 0x2444B60F:  // 0F B6 44 24 XX = movzx eax, byte ptr [esp+XXh]
-        cursor += 5;
-        continue;
-    }
+    size_t instruction_size = GetInstructionSize(address + cursor);
+    if (!instruction_size)
+      return 0;
+    cursor += instruction_size;
+  }
+  return cursor;
+}
 
-    // Unknown instruction!
-    // FIXME: Unknown instruction failures might happen when we add a new
-    // interceptor or a new compiler version. In either case, they should result
-    // in visible and readable error messages. However, merely calling abort()
-    // leads to an infinite recursion in CheckFailed.
-    // Do we have a good way to abort with an error message here?
-    __debugbreak();
-    return 0;
+#if !SANITIZER_WINDOWS64
+bool OverrideFunctionWithDetour(
+    uptr old_func, uptr new_func, uptr *orig_old_func) {
+  const int kDetourHeaderLen = 5;
+  const u16 kDetourInstruction = 0xFF8B;
+
+  uptr header = (uptr)old_func - kDetourHeaderLen;
+  uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength;
+
+  // Validate that the function is hookable.
+  if (*(u16*)old_func != kDetourInstruction ||
+      !IsMemoryPadding(header, kDetourHeaderLen))
+    return false;
+
+  // Change memory protection to writable.
+  DWORD protection = 0;
+  if (!ChangeMemoryProtection(header, patch_length, &protection))
+    return false;
+
+  // Write a relative jump to the redirected function.
+  WriteJumpInstruction(header, new_func);
+
+  // Write the short jump to the function prefix.
+  WriteShortJumpInstruction(old_func, header);
+
+  // Restore previous memory protection.
+  if (!RestoreMemoryProtection(header, patch_length, protection))
+    return false;
+
+  if (orig_old_func)
+    *orig_old_func = old_func + kShortJumpInstructionLength;
+
+  return true;
+}
+#endif
+
+bool OverrideFunctionWithRedirectJump(
+    uptr old_func, uptr new_func, uptr *orig_old_func) {
+  // Check whether the first instruction is a relative jump.
+  if (*(u8*)old_func != 0xE9)
+    return false;
+
+  if (orig_old_func) {
+    uptr relative_offset = *(u32*)(old_func + 1);
+    uptr absolute_target = old_func + relative_offset + kJumpInstructionLength;
+    *orig_old_func = absolute_target;
   }
 
-  return cursor;
+#if SANITIZER_WINDOWS64
+  // If needed, get memory space for a trampoline jump.
+  uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength);
+  if (!trampoline)
+    return false;
+  WriteDirectBranch(trampoline, new_func);
 #endif
+
+  // Change memory protection to writable.
+  DWORD protection = 0;
+  if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection))
+    return false;
+
+  // Write a relative jump to the redirected function.
+  WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline));
+
+  // Restore previous memory protection.
+  if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection))
+    return false;
+
+  return true;
 }
 
-bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
-  // Function overriding works basically like this:
-  // On Win32, We write "jmp <new_func>" (5 bytes) at the beginning of
-  // the 'old_func' to override it.
-  // On Win64, We write "jmp [rip -8]" (6 bytes) at the beginning of
-  // the 'old_func' to override it, and use 8 bytes of data to store
-  // the full 64-bit address for new_func.
-  // We might want to be able to execute the original 'old_func' from the
-  // wrapper, in this case we need to keep the leading 5+ (6+ on Win64)
-  // bytes ('head') of the original code somewhere with a "jmp <old_func+head>".
-  // We call these 'head'+5/6 bytes of instructions a "trampoline".
-  char *old_bytes = (char *)old_func;
+bool OverrideFunctionWithHotPatch(
+    uptr old_func, uptr new_func, uptr *orig_old_func) {
+  const int kHotPatchHeaderLen = kBranchLength;
+
+  uptr header = (uptr)old_func - kHotPatchHeaderLen;
+  uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength;
+
+  // Validate that the function is hot patchable.
+  size_t instruction_size = GetInstructionSize(old_func);
+  if (instruction_size < kShortJumpInstructionLength ||
+      !IsMemoryPadding(header, kHotPatchHeaderLen))
+    return false;
+
+  if (orig_old_func) {
+    // Put the needed instructions into the trampoline bytes.
+    uptr trampoline_length = instruction_size + kDirectBranchLength;
+    uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
+    if (!trampoline)
+      return false;
+    CopyInstructions(trampoline, old_func, instruction_size);
+    WriteDirectBranch(trampoline + instruction_size,
+                      old_func + instruction_size);
+    *orig_old_func = trampoline;
+  }
 
+  // If needed, get memory space for indirect address.
+  uptr indirect_address = 0;
 #if SANITIZER_WINDOWS64
-  size_t kHeadMin = 6;  // The minimum size of the head to contain the 'jmp'.
-  size_t kTrampolineJumpSize = 14;  // The total bytes used at the end of
-                                    // trampoline for jumping back to the
-                                    // remains of original function.
-  size_t kExtraPrevBytes = 8;  // The extra bytes we need to mark READWRITE for
-                               // page access, that is preceeding the begin
-                               // of function.
-#else
-  size_t kHeadMin = 5;
-  size_t kTrampolineJumpSize = 5;
-  size_t kExtraPrevBytes = 0;
+  indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
+  if (!indirect_address)
+    return false;
 #endif
-  size_t head = kHeadMin;
+
+  // Change memory protection to writable.
+  DWORD protection = 0;
+  if (!ChangeMemoryProtection(header, patch_length, &protection))
+    return false;
+
+  // Write jumps to the redirected function.
+  WriteBranch(header, indirect_address, new_func);
+  WriteShortJumpInstruction(old_func, header);
+
+  // Restore previous memory protection.
+  if (!RestoreMemoryProtection(header, patch_length, protection))
+    return false;
+
+  return true;
+}
+
+bool OverrideFunctionWithTrampoline(
+    uptr old_func, uptr new_func, uptr *orig_old_func) {
+
+  size_t instructions_length = kBranchLength;
+  size_t padding_length = 0;
+  uptr indirect_address = 0;
+
   if (orig_old_func) {
     // Find out the number of bytes of the instructions we need to copy
-    // to the trampoline and store it in 'head'.
-    head = RoundUpToInstrBoundary(kHeadMin, old_bytes);
-    if (!head)
+    // to the trampoline.
+    instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func);
+    if (!instructions_length)
       return false;
 
     // Put the needed instructions into the trampoline bytes.
-    char *trampoline = GetMemoryForTrampoline(head + kTrampolineJumpSize);
+    uptr trampoline_length = instructions_length + kDirectBranchLength;
+    uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
     if (!trampoline)
       return false;
-    _memcpy(trampoline, old_bytes, head);
-    WriteTrampolineJumpInstruction(trampoline + head, old_bytes + head);
-    *orig_old_func = (uptr)trampoline;
-  }
-
-  // Now put the "jmp <new_func>" instruction at the original code location.
-  // We should preserve the EXECUTE flag as some of our own code might be
-  // located in the same page (sic!).  FIXME: might consider putting the
-  // __interception code into a separate section or something?
-  DWORD old_prot, unused_prot;
-  // TODO(wwchrome): Properly handle access violations when finding a safe
-  // region to store the indirect jump target address.
-  // Need to mark extra 8 bytes for Win64 because jmp [rip -8]
-  if (!VirtualProtect((void *)(old_bytes - kExtraPrevBytes),
-                      head + kExtraPrevBytes, PAGE_EXECUTE_READWRITE,
-                      &old_prot))
-    return false;
-
-  WriteInterceptorJumpInstruction(old_bytes, (char *)new_func);
-  _memset(old_bytes + kHeadMin, 0xCC /* int 3 */, head - kHeadMin);
-
-  // Restore the original permissions.
-  if (!VirtualProtect((void *)(old_bytes - kExtraPrevBytes),
-                      head + kExtraPrevBytes, old_prot, &unused_prot))
-    return false;  // not clear if this failure bothers us.
+    CopyInstructions(trampoline, old_func, instructions_length);
+    WriteDirectBranch(trampoline + instructions_length,
+                      old_func + instructions_length);
+    *orig_old_func = trampoline;
+  }
+
+#if SANITIZER_WINDOWS64
+  // Check if the targeted address can be encoded in the function padding.
+  // Otherwise, allocate it in the trampoline region.
+  if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) {
+    indirect_address = old_func - kAddressLength;
+    padding_length = kAddressLength;
+  } else {
+    indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
+    if (!indirect_address)
+      return false;
+  }
+#endif
+
+  // Change memory protection to writable.
+  uptr patch_address = old_func - padding_length;
+  uptr patch_length = instructions_length + padding_length;
+  DWORD protection = 0;
+  if (!ChangeMemoryProtection(patch_address, patch_length, &protection))
+    return false;
+
+  // Patch the original function.
+  WriteBranch(old_func, indirect_address, new_func);
+
+  // Restore previous memory protection.
+  if (!RestoreMemoryProtection(patch_address, patch_length, protection))
+    return false;
 
   return true;
 }
 
+bool OverrideFunction(
+    uptr old_func, uptr new_func, uptr *orig_old_func) {
+#if !SANITIZER_WINDOWS64
+  if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func))
+    return true;
+#endif
+  if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func))
+    return true;
+  if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func))
+    return true;
+  if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func))
+    return true;
+  return false;
+}
+
 static void **InterestingDLLsAvailable() {
   static const char *InterestingDLLs[] = {
       "kernel32.dll",
@@ -461,7 +824,7 @@ bool OverrideImportedFunction(const char
   RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
   RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
   if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
-      headers->Signature != IMAGE_NT_SIGNATURE ||           // "PE\0\0"
+      headers->Signature != IMAGE_NT_SIGNATURE ||            // "PE\0\0"
       headers->FileHeader.SizeOfOptionalHeader <
           sizeof(IMAGE_OPTIONAL_HEADER)) {
     return false;

Modified: compiler-rt/trunk/lib/interception/interception_win.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/interception_win.h?rev=275123&r1=275122&r2=275123&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception_win.h (original)
+++ compiler-rt/trunk/lib/interception/interception_win.h Mon Jul 11 18:02:18 2016
@@ -42,6 +42,23 @@ bool OverrideImportedFunction(const char
                               const char *function_name, uptr new_function,
                               uptr *orig_old_func);
 
+#if !SANITIZER_WINDOWS64
+// Exposed for unittests
+bool OverrideFunctionWithDetour(
+    uptr old_func, uptr new_func, uptr *orig_old_func);
+#endif
+
+// Exposed for unittests
+bool OverrideFunctionWithRedirectJump(
+    uptr old_func, uptr new_func, uptr *orig_old_func);
+bool OverrideFunctionWithHotPatch(
+    uptr old_func, uptr new_func, uptr *orig_old_func);
+bool OverrideFunctionWithTrampoline(
+    uptr old_func, uptr new_func, uptr *orig_old_func);
+
+// Exposed for unittests
+void TestOnlyReleaseTrampolineRegions();
+
 }  // namespace __interception
 
 #if defined(INTERCEPTION_DYNAMIC_CRT)

Modified: compiler-rt/trunk/lib/interception/tests/interception_win_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/interception/tests/interception_win_test.cc?rev=275123&r1=275122&r2=275123&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/tests/interception_win_test.cc (original)
+++ compiler-rt/trunk/lib/interception/tests/interception_win_test.cc Mon Jul 11 18:02:18 2016
@@ -22,32 +22,121 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
+namespace __interception {
 namespace {
 
+enum FunctionPrefixKind {
+  FunctionPrefixNone,
+  FunctionPrefixPadding,
+  FunctionPrefixHotPatch,
+  FunctionPrefixDetour,
+};
+
+typedef bool (*TestOverrideFunction)(uptr, uptr, uptr*);
 typedef int (*IdentityFunction)(int);
 
-#if !SANITIZER_WINDOWS64
+#if SANITIZER_WINDOWS64
 
 const u8 kIdentityCodeWithPrologue[] = {
-    0x55,              // push        ebp
-    0x8B, 0xEC,        // mov         ebp,esp
-    0x8B, 0x45, 0x08,  // mov         eax,dword ptr [ebp + 8]
-    0x5D,              // pop         ebp
-    0xC3,              // ret
+    0x55,                   // push        rbp
+    0x48, 0x89, 0xE5,       // mov         rbp,rsp
+    0x8B, 0xC1,             // mov         eax,ecx
+    0x5D,                   // pop         rbp
+    0xC3,                   // ret
 };
 
 const u8 kIdentityCodeWithPushPop[] = {
-    0x55,              // push        ebp
-    0x8B, 0xEC,        // mov         ebp,esp
-    0x53,              // push        ebx
-    0x50,              // push        eax
-    0x58,              // pop         eax
-    0x8B, 0x45, 0x08,  // mov         eax,dword ptr [ebp + 8]
-    0x5B,              // pop         ebx
-    0x5D,              // pop         ebp
-    0xC3,              // ret
+    0x55,                   // push        rbp
+    0x48, 0x89, 0xE5,       // mov         rbp,rsp
+    0x53,                   // push        rbx
+    0x50,                   // push        rax
+    0x58,                   // pop         rax
+    0x8B, 0xC1,             // mov         rax,rcx
+    0x5B,                   // pop         rbx
+    0x5D,                   // pop         rbp
+    0xC3,                   // ret
+};
+
+const u8 kIdentityTwiceOffset = 16;
+const u8 kIdentityTwice[] = {
+    0x55,                   // push        rbp
+    0x48, 0x89, 0xE5,       // mov         rbp,rsp
+    0x8B, 0xC1,             // mov         eax,ecx
+    0x5D,                   // pop         rbp
+    0xC3,                   // ret
+    0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90,
+    0x55,                   // push        rbp
+    0x48, 0x89, 0xE5,       // mov         rbp,rsp
+    0x8B, 0xC1,             // mov         eax,ecx
+    0x5D,                   // pop         rbp
+    0xC3,                   // ret
 };
 
+const u8 kIdentityCodeWithMov[] = {
+    0x89, 0xC8,             // mov         eax, ecx
+    0xC3,                   // ret
+};
+
+const u8 kIdentityCodeWithJump[] = {
+    0xE9, 0x04, 0x00, 0x00,
+    0x00,                   // jmp + 4
+    0xCC, 0xCC, 0xCC, 0xCC,
+    0x89, 0xC8,             // mov         eax, ecx
+    0xC3,                   // ret
+};
+
+#else
+
+const u8 kIdentityCodeWithPrologue[] = {
+    0x55,                   // push        ebp
+    0x8B, 0xEC,             // mov         ebp,esp
+    0x8B, 0x45, 0x08,       // mov         eax,dword ptr [ebp + 8]
+    0x5D,                   // pop         ebp
+    0xC3,                   // ret
+};
+
+const u8 kIdentityCodeWithPushPop[] = {
+    0x55,                   // push        ebp
+    0x8B, 0xEC,             // mov         ebp,esp
+    0x53,                   // push        ebx
+    0x50,                   // push        eax
+    0x58,                   // pop         eax
+    0x8B, 0x45, 0x08,       // mov         eax,dword ptr [ebp + 8]
+    0x5B,                   // pop         ebx
+    0x5D,                   // pop         ebp
+    0xC3,                   // ret
+};
+
+const u8 kIdentityTwiceOffset = 8;
+const u8 kIdentityTwice[] = {
+    0x55,                   // push        ebp
+    0x8B, 0xEC,             // mov         ebp,esp
+    0x8B, 0x45, 0x08,       // mov         eax,dword ptr [ebp + 8]
+    0x5D,                   // pop         ebp
+    0xC3,                   // ret
+    0x55,                   // push        ebp
+    0x8B, 0xEC,             // mov         ebp,esp
+    0x8B, 0x45, 0x08,       // mov         eax,dword ptr [ebp + 8]
+    0x5D,                   // pop         ebp
+    0xC3,                   // ret
+};
+
+const u8 kIdentityCodeWithMov[] = {
+    0x8B, 0x44, 0x24, 0x04, // mov         eax,dword ptr [esp + 4]
+    0xC3,                   // ret
+};
+
+const u8 kIdentityCodeWithJump[] = {
+    0xE9, 0x04, 0x00, 0x00,
+    0x00,                   // jmp + 4
+    0xCC, 0xCC, 0xCC, 0xCC,
+    0x8B, 0x44, 0x24, 0x04, // mov         eax,dword ptr [esp + 4]
+    0xC3,                   // ret
+};
+
+#endif
+
 const u8 kPatchableCode1[] = {
     0xB8, 0x4B, 0x00, 0x00, 0x00,   // mov eax,4B
     0x33, 0xC9,                     // xor ecx,ecx
@@ -69,6 +158,11 @@ const u8 kPatchableCode3[] = {
     0xE8, 0x3D, 0xFF, 0xFF, 0xFF,   // call <func>
 };
 
+const u8 kPatchableCode4[] = {
+    0xE9, 0xCC, 0xCC, 0xCC, 0xCC,   // jmp <label>
+    0x90, 0x90, 0x90, 0x90,
+};
+
 const u8 kUnpatchableCode1[] = {
     0xC3,                           // ret
 };
@@ -97,49 +191,68 @@ const u8 kUnpatchableCode5[] = {
 };
 
 const u8 kUnpatchableCode6[] = {
-    0xE9, 0xCC, 0xCC, 0xCC, 0xCC,   // jmp <label>
-    0x90, 0x90, 0x90, 0x90,
-};
-
-const u8 kUnpatchableCode7[] = {
     0xE8, 0xCC, 0xCC, 0xCC, 0xCC,   // call <func>
     0x90, 0x90, 0x90, 0x90,
 };
 
-#endif
-
 // A buffer holding the dynamically generated code under test.
 u8* ActiveCode;
 size_t ActiveCodeLength = 4096;
 
 template<class T>
-void LoadActiveCode(const T &Code, uptr *EntryPoint) {
+static void LoadActiveCode(
+    const T &code,
+    uptr *entry_point,
+    FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
   if (ActiveCode == nullptr) {
     ActiveCode =
-        (u8*)::VirtualAlloc(nullptr, ActiveCodeLength, MEM_COMMIT | MEM_RESERVE,
+        (u8*)::VirtualAlloc(nullptr, ActiveCodeLength,
+                            MEM_COMMIT | MEM_RESERVE,
                             PAGE_EXECUTE_READWRITE);
     ASSERT_NE(ActiveCode, nullptr);
   }
 
-  size_t Position = 0;
-  *EntryPoint = (uptr)&ActiveCode[0];
+  size_t position = 0;
+
+  // Add padding to avoid memory violation when scanning the prefix.
+  for (int i = 0; i < 16; ++i)
+    ActiveCode[position++] = 0xC3;  // Instruction 'ret'.
+
+  // Add function padding.
+  size_t padding = 0;
+  if (prefix_kind == FunctionPrefixPadding)
+    padding = 16;
+  else if (prefix_kind == FunctionPrefixDetour ||
+           prefix_kind == FunctionPrefixHotPatch)
+    padding = FIRST_32_SECOND_64(5, 6);
+  // Insert |padding| instructions 'nop'.
+  for (size_t i = 0; i < padding; ++i)
+    ActiveCode[position++] = 0x90;
+
+  // Keep track of the entry point.
+  *entry_point = (uptr)&ActiveCode[position];
+
+  // Add the detour instruction (i.e. mov edi, edi)
+  if (prefix_kind == FunctionPrefixDetour) {
+    ActiveCode[position++] = 0x8B;
+    ActiveCode[position++] = 0xFF;
+  }
 
   // Copy the function body.
   for (size_t i = 0; i < sizeof(T); ++i)
-    ActiveCode[Position++] = Code[i];
+    ActiveCode[position++] = code[i];
 }
 
 int InterceptorFunctionCalled;
+IdentityFunction InterceptedRealFunction;
 
-NOINLINE int InterceptorFunction(int x) {
+int InterceptorFunction(int x) {
   ++InterceptorFunctionCalled;
-  return x;
+  return InterceptedRealFunction(x);
 }
 
 }  // namespace
 
-namespace __interception {
-
 // Tests for interception_win.h
 TEST(Interception, InternalGetProcAddress) {
   HMODULE ntdll_handle = ::GetModuleHandle("ntdll");
@@ -155,67 +268,313 @@ TEST(Interception, InternalGetProcAddres
 }
 
 template<class T>
-bool TestFunctionPatching(const T &Code) {
-  uptr Address;
-  int x = sizeof(T);
-
-  LoadActiveCode<T>(Code, &Address);
-  uptr UnusedRealAddress = 0;
-  return OverrideFunction(Address, (uptr)&InterceptorFunction,
-                          &UnusedRealAddress);
-}
-
-template<class T>
-void TestIdentityFunctionPatching(const T &IdentityCode) {
-  uptr IdentityAddress;
-  LoadActiveCode<T>(IdentityCode, &IdentityAddress);
-  IdentityFunction Identity = (IdentityFunction)IdentityAddress;
+static void TestIdentityFunctionPatching(
+    const T &code,
+    TestOverrideFunction override,
+    FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
+  uptr identity_address;
+  LoadActiveCode(code, &identity_address, prefix_kind);
+  IdentityFunction identity = (IdentityFunction)identity_address;
 
   // Validate behavior before dynamic patching.
   InterceptorFunctionCalled = 0;
-  EXPECT_EQ(0, Identity(0));
-  EXPECT_EQ(42, Identity(42));
+  EXPECT_EQ(0, identity(0));
+  EXPECT_EQ(42, identity(42));
   EXPECT_EQ(0, InterceptorFunctionCalled);
 
   // Patch the function.
-  uptr RealIdentityAddress = 0;
-  EXPECT_TRUE(OverrideFunction(IdentityAddress, (uptr)&InterceptorFunction,
-                               &RealIdentityAddress));
-  IdentityFunction RealIdentity = (IdentityFunction)RealIdentityAddress;
+  uptr real_identity_address = 0;
+  bool success = override(identity_address,
+                         (uptr)&InterceptorFunction,
+                         &real_identity_address);
+  EXPECT_TRUE(success);
+  EXPECT_NE(0U, real_identity_address);
+  IdentityFunction real_identity = (IdentityFunction)real_identity_address;
+  InterceptedRealFunction = real_identity;
+
+  // Don't run tests if hooking failed or the real function is not valid.
+  if (!success || !real_identity_address)
+    return;
 
   // Calling the redirected function.
   InterceptorFunctionCalled = 0;
-  EXPECT_EQ(0, Identity(0));
-  EXPECT_EQ(42, Identity(42));
+  EXPECT_EQ(0, identity(0));
+  EXPECT_EQ(42, identity(42));
   EXPECT_EQ(2, InterceptorFunctionCalled);
 
   // Calling the real function.
   InterceptorFunctionCalled = 0;
-  EXPECT_EQ(0, RealIdentity(0));
-  EXPECT_EQ(42, RealIdentity(42));
+  EXPECT_EQ(0, real_identity(0));
+  EXPECT_EQ(42, real_identity(42));
   EXPECT_EQ(0, InterceptorFunctionCalled);
+
+  TestOnlyReleaseTrampolineRegions();
 }
 
 #if !SANITIZER_WINDOWS64
+TEST(Interception, OverrideFunctionWithDetour) {
+  TestOverrideFunction override = OverrideFunctionWithDetour;
+  FunctionPrefixKind prefix = FunctionPrefixDetour;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
+}
+#endif  // !SANITIZER_WINDOWS64
+
+TEST(Interception, OverrideFunctionWithRedirectJump) {
+  TestOverrideFunction override = OverrideFunctionWithRedirectJump;
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override);
+}
+
+TEST(Interception, OverrideFunctionWithHotPatch) {
+  TestOverrideFunction override = OverrideFunctionWithHotPatch;
+  FunctionPrefixKind prefix = FunctionPrefixHotPatch;
+  TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
+}
+
+TEST(Interception, OverrideFunctionWithTrampoline) {
+  TestOverrideFunction override = OverrideFunctionWithTrampoline;
+  FunctionPrefixKind prefix = FunctionPrefixNone;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+
+  prefix = FunctionPrefixPadding;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+}
+
 TEST(Interception, OverrideFunction) {
-  TestIdentityFunctionPatching(kIdentityCodeWithPrologue);
-  TestIdentityFunctionPatching(kIdentityCodeWithPushPop);
+  TestOverrideFunction override = OverrideFunction;
+  FunctionPrefixKind prefix = FunctionPrefixNone;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
+
+  prefix = FunctionPrefixPadding;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
+
+  prefix = FunctionPrefixHotPatch;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
+
+  prefix = FunctionPrefixDetour;
+  TestIdentityFunctionPatching(kIdentityCodeWithPrologue, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithPushPop, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithMov, override, prefix);
+  TestIdentityFunctionPatching(kIdentityCodeWithJump, override, prefix);
+}
+
+template<class T>
+static void TestIdentityFunctionMultiplePatching(
+    const T &code,
+    TestOverrideFunction override,
+    FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
+  uptr identity_address;
+  LoadActiveCode(code, &identity_address, prefix_kind);
+
+  // Patch the function.
+  uptr real_identity_address = 0;
+  bool success = override(identity_address,
+                          (uptr)&InterceptorFunction,
+                          &real_identity_address);
+  EXPECT_TRUE(success);
+  EXPECT_NE(0U, real_identity_address);
+
+  // Re-patching the function should not work.
+  success = override(identity_address,
+                     (uptr)&InterceptorFunction,
+                     &real_identity_address);
+  EXPECT_FALSE(success);
+
+  TestOnlyReleaseTrampolineRegions();
+}
+
+TEST(Interception, OverrideFunctionMultiplePatchingIsFailing) {
+#if !SANITIZER_WINDOWS64
+  TestIdentityFunctionMultiplePatching(kIdentityCodeWithPrologue,
+                                       OverrideFunctionWithDetour,
+                                       FunctionPrefixDetour);
+#endif
+
+  TestIdentityFunctionMultiplePatching(kIdentityCodeWithMov,
+                                       OverrideFunctionWithHotPatch,
+                                       FunctionPrefixHotPatch);
+
+  TestIdentityFunctionMultiplePatching(kIdentityCodeWithPushPop,
+                                       OverrideFunctionWithTrampoline,
+                                       FunctionPrefixPadding);
+}
+
+TEST(Interception, OverrideFunctionTwice) {
+  uptr identity_address1;
+  LoadActiveCode(kIdentityTwice, &identity_address1);
+  uptr identity_address2 = identity_address1 + kIdentityTwiceOffset;
+  IdentityFunction identity1 = (IdentityFunction)identity_address1;
+  IdentityFunction identity2 = (IdentityFunction)identity_address2;
+
+  // Patch the two functions.
+  uptr real_identity_address = 0;
+  EXPECT_TRUE(OverrideFunction(identity_address1,
+                               (uptr)&InterceptorFunction,
+                               &real_identity_address));
+  EXPECT_TRUE(OverrideFunction(identity_address2,
+                               (uptr)&InterceptorFunction,
+                               &real_identity_address));
+  IdentityFunction real_identity = (IdentityFunction)real_identity_address;
+  InterceptedRealFunction = real_identity;
+
+  // Calling the redirected function.
+  InterceptorFunctionCalled = 0;
+  EXPECT_EQ(42, identity1(42));
+  EXPECT_EQ(42, identity2(42));
+  EXPECT_EQ(2, InterceptorFunctionCalled);
+
+  TestOnlyReleaseTrampolineRegions();
+}
+
+template<class T>
+static bool TestFunctionPatching(
+    const T &code,
+    TestOverrideFunction override,
+    FunctionPrefixKind prefix_kind = FunctionPrefixNone) {
+  uptr address;
+  LoadActiveCode(code, &address, prefix_kind);
+  uptr unused_real_address = 0;
+  bool result = override(
+      address, (uptr)&InterceptorFunction, &unused_real_address);
+
+  TestOnlyReleaseTrampolineRegions();
+  return result;
 }
 
 TEST(Interception, PatchableFunction) {
-  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1));
-  EXPECT_TRUE(TestFunctionPatching(kPatchableCode2));
-  EXPECT_TRUE(TestFunctionPatching(kPatchableCode3));
-
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6));
-  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode7));
+  TestOverrideFunction override = OverrideFunction;
+  // Test without function padding.
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override));
+#if SANITIZER_WINDOWS64
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
+#else
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override));
+#endif
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override));
+
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
+}
+
+#if !SANITIZER_WINDOWS64
+TEST(Interception, PatchableFunctionWithDetour) {
+  TestOverrideFunction override = OverrideFunctionWithDetour;
+  // Without the prefix, no function can be detoured.
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
+
+  // With the prefix, all functions can be detoured.
+  FunctionPrefixKind prefix = FunctionPrefixDetour;
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
+}
+#endif  // !SANITIZER_WINDOWS64
+
+TEST(Interception, PatchableFunctionWithRedirectJump) {
+  TestOverrideFunction override = OverrideFunctionWithRedirectJump;
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode1, override));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override));
+}
+
+TEST(Interception, PatchableFunctionWithHotPatch) {
+  TestOverrideFunction override = OverrideFunctionWithHotPatch;
+  FunctionPrefixKind prefix = FunctionPrefixHotPatch;
+
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode2, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix));
+
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
+}
+
+TEST(Interception, PatchableFunctionWithTrampoline) {
+  TestOverrideFunction override = OverrideFunctionWithTrampoline;
+  FunctionPrefixKind prefix = FunctionPrefixPadding;
+
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
+#if SANITIZER_WINDOWS64
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
+#else
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
+#endif
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode4, override, prefix));
+
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
 }
+
+TEST(Interception, PatchableFunctionPadding) {
+  TestOverrideFunction override = OverrideFunction;
+  FunctionPrefixKind prefix = FunctionPrefixPadding;
+
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode2, override, prefix));
+#if SANITIZER_WINDOWS64
+  EXPECT_FALSE(TestFunctionPatching(kPatchableCode3, override, prefix));
+#else
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode3, override, prefix));
 #endif
+  EXPECT_TRUE(TestFunctionPatching(kPatchableCode4, override, prefix));
+
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode1, override, prefix));
+  EXPECT_TRUE(TestFunctionPatching(kUnpatchableCode2, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode3, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode4, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode5, override, prefix));
+  EXPECT_FALSE(TestFunctionPatching(kUnpatchableCode6, override, prefix));
+}
 
 }  // namespace __interception
 




More information about the llvm-commits mailing list