[compiler-rt] r271915 - This patch attempts to primitive support for Win64 asan

Etienne Bergeron via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 6 11:09:54 PDT 2016


Author: etienneb
Date: Mon Jun  6 13:09:54 2016
New Revision: 271915

URL: http://llvm.org/viewvc/llvm-project?rev=271915&view=rev
Log:
This patch attempts to primitive support for Win64 asan

Some known issues are:

When "head" include instructions that involve branching, the "cut and paste" approach may break down in a way that function interception still work but calling back the original function does not work.
The jmp [rip -8] saves some bytes in the "head" but finding the safe zone of 0xCC is not implemented yet. So it may stomp on preceding codes.
The shadow offset is not working yet on Win64. More complexity maybe involved since there are some differences regarding virtual address space between Window 8 and Windows 8.1/10.

Patch by: Wang Wei

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


Modified:
    compiler-rt/trunk/lib/asan/asan_internal.h
    compiler-rt/trunk/lib/asan/asan_rtl.cc
    compiler-rt/trunk/lib/interception/interception_win.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h

Modified: compiler-rt/trunk/lib/asan/asan_internal.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_internal.h?rev=271915&r1=271914&r2=271915&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_internal.h (original)
+++ compiler-rt/trunk/lib/asan/asan_internal.h Mon Jun  6 13:09:54 2016
@@ -102,6 +102,8 @@ void ReserveShadowMemoryRange(uptr beg,
 bool PlatformHasDifferentMemcpyAndMemmove();
 # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
     (PlatformHasDifferentMemcpyAndMemmove())
+#elif SANITIZER_WINDOWS64
+# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
 #else
 # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
 #endif  // SANITIZER_MAC

Modified: compiler-rt/trunk/lib/asan/asan_rtl.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_rtl.cc?rev=271915&r1=271914&r2=271915&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_rtl.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_rtl.cc Mon Jun  6 13:09:54 2016
@@ -463,6 +463,12 @@ static void AsanInitInternal() {
     kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
     kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
   }
+#elif SANITIZER_WINDOWS64
+  // Disable the "mid mem" shadow layout.
+  if (!full_shadow_is_available) {
+    kMidMemBeg = 0;
+    kMidMemEnd = 0;
+  }
 #endif
 
   if (Verbosity()) PrintAddressSpaceLayout();

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=271915&r1=271914&r2=271915&view=diff
==============================================================================
--- compiler-rt/trunk/lib/interception/interception_win.cc (original)
+++ compiler-rt/trunk/lib/interception/interception_win.cc Mon Jun  6 13:09:54 2016
@@ -35,6 +35,18 @@ static void _memcpy(void *dst, void *src
     dst_c[i] = src_c[i];
 }
 
+#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;
+}
+#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.
@@ -42,6 +54,39 @@ static void WriteJumpInstruction(char *j
   *jmp_from = '\xE9';
   *(ptrdiff_t*)(jmp_from + 1) = offset;
 }
+#endif
+
+static void WriteTrampolineJumpInstruction(char *jmp_from, char *to) {
+#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);
+#else
+  WriteJumpInstruction(jmp_from, to);
+#endif
+}
+
+static void WriteInterceptorJumpInstruction(char *jmp_from, char *to) {
+#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);
+#else
+  WriteJumpInstruction(jmp_from, to);
+#endif
+}
 
 static char *GetMemoryForTrampoline(size_t size) {
   // Trampolines are allocated from a common pool.
@@ -68,11 +113,74 @@ static char *GetMemoryForTrampoline(size
 
 // Returns 0 on error.
 static size_t RoundUpToInstrBoundary(size_t size, char *code) {
-#ifdef _WIN64
-  // TODO(wwchrome): Implement similar logic for x64 instructions.
-  // Win64 RoundUpToInstrBoundary is not supported yet.
-  __debugbreak();
-  return 0;
+#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 '\xb8':  // b8 XX XX XX XX : mov eax, XX XX XX XX
+        cursor += 5;
+        continue;
+    }
+
+    switch (*(unsigned short*)(code + cursor)) {  // NOLINT
+      case 0x5540:  // 40 55 : rex push rbp
+      case 0x5340:  // 40 53 : rex push rbx
+        cursor += 2;
+        continue;
+    }
+
+    switch (0x00FFFFFF & *(unsigned int*)(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;
+    }
+
+    // Check first 5 bytes.
+    switch (0xFFFFFFFFFFull & *(unsigned long long*)(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;
+    }
+
+    // Unknown instructions!
+    __debugbreak();
+  }
+
+  return cursor;
 #else
   size_t cursor = 0;
   while (cursor < size) {
@@ -150,36 +258,45 @@ static size_t RoundUpToInstrBoundary(siz
 }
 
 bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
-#ifdef _WIN64
-  // TODO(wwchrome): Implement using x64 jmp.
-  // OverrideFunction is not yet supported on x64.
-  __debugbreak();
-  return false;
-#else
   // Function overriding works basically like this:
-  // We write "jmp <new_func>" (5 bytes) at the beginning of the 'old_func'
-  // to override it.
+  // 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+ bytes ('head')
-  // of the original code somewhere with a "jmp <old_func+head>".
-  // We call these 'head'+5 bytes of instructions a "trampoline".
+  // 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;
 
-  // We'll need at least 5 bytes for a 'jmp'.
-  size_t head = 5;
+#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;
+#endif
+  size_t head = kHeadMin;
   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(head, old_bytes);
+    head = RoundUpToInstrBoundary(kHeadMin, old_bytes);
     if (!head)
       return false;
 
     // Put the needed instructions into the trampoline bytes.
-    char *trampoline = GetMemoryForTrampoline(head + 5);
+    char *trampoline = GetMemoryForTrampoline(head + kTrampolineJumpSize);
     if (!trampoline)
       return false;
     _memcpy(trampoline, old_bytes, head);
-    WriteJumpInstruction(trampoline + head, old_bytes + head);
+    WriteTrampolineJumpInstruction(trampoline + head, old_bytes + head);
     *orig_old_func = (uptr)trampoline;
   }
 
@@ -188,19 +305,23 @@ bool OverrideFunction(uptr old_func, upt
   // located in the same page (sic!).  FIXME: might consider putting the
   // __interception code into a separate section or something?
   DWORD old_prot, unused_prot;
-  if (!VirtualProtect((void *)old_bytes, head, PAGE_EXECUTE_READWRITE,
+  // 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;
 
-  WriteJumpInstruction(old_bytes, (char *)new_func);
-  _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5);
+  WriteInterceptorJumpInstruction(old_bytes, (char *)new_func);
+  _memset(old_bytes + kHeadMin, 0xCC /* int 3 */, head - kHeadMin);
 
   // Restore the original permissions.
-  if (!VirtualProtect((void *)old_bytes, head, old_prot, &unused_prot))
+  if (!VirtualProtect((void *)(old_bytes - kExtraPrevBytes),
+                      head + kExtraPrevBytes, old_prot, &unused_prot))
     return false;  // not clear if this failure bothers us.
 
   return true;
-#endif
 }
 
 static void **InterestingDLLsAvailable() {

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform.h?rev=271915&r1=271914&r2=271915&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform.h Mon Jun  6 13:09:54 2016
@@ -67,6 +67,12 @@
 # define SANITIZER_WINDOWS 0
 #endif
 
+#if defined(_WIN64)
+# define SANITIZER_WINDOWS64 1
+#else
+# define SANITIZER_WINDOWS64 0
+#endif
+
 #if defined(__ANDROID__)
 # define SANITIZER_ANDROID 1
 #else

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h?rev=271915&r1=271914&r2=271915&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h Mon Jun  6 13:09:54 2016
@@ -83,7 +83,15 @@
 #define SANITIZER_INTERCEPT_MEMMOVE 1
 #define SANITIZER_INTERCEPT_MEMCPY 1
 #define SANITIZER_INTERCEPT_MEMCMP 1
+// TODO(wwchrome): Re-enable intercepting memchr() when ready.
+// The function memchr() contains a jump in the first 6 bytes
+// that is problematic to intercept correctly on Win64.
+// Disable memchr() interception for Win64 temporarily.
+#if SANITIZER_WINDOWS64
+#define SANITIZER_INTERCEPT_MEMCHR 0
+#else
 #define SANITIZER_INTERCEPT_MEMCHR 1
+#endif
 #define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX
 
 #define SANITIZER_INTERCEPT_READ   SI_NOT_WINDOWS




More information about the llvm-commits mailing list