Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[asan][Windows] Add additional wcs* interceptors #66128

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

strega-nil-ms
Copy link

@strega-nil-ms strega-nil-ms commented Sep 12, 2023

This adds wcs[n]cat, wcs[n]cmp, wcs[n]cpy, and wcschr functions to the interception code on Windows; wcs[n]cat was already intercepted, but only on POSIX.

This was originally done in LLVM-D157038. However, I made the wrapper functions call REAL(blah), which had been munged in a way that no longer worked, which broke them. Thankfully, the tests caught this issue, so I was able to revert, and now I'm sending this back with the change fixed.

Additionally, as part of this change, I added some new cases to interception_win.cpp; as part of that, I did a sort on the cases, so that we could update these statements in the future without worrying about the ordering between our internal fork (which we are working on upstreaming), and LLVM's.

Depends on #66529

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 12, 2023

@llvm/pr-subscribers-compiler-rt-sanitizer

Changes

This adds wcs[n]cat, wcs[n]cmp, wcs[n]cpy, and wcschr functions to the interception code on Windows; wcs[n]cat was already intercepted, but only on POSIX.

This was originally done in LLVM-D157038. However, I made the wrapper functions call REAL(blah), which had been munged in a way that no longer worked, which broke them. Thankfully, the tests caught this issue, so I was able to revert, and now I'm sending this back with the change fixed.

Additionally, as part of this change, I added some new cases to interception_win.cpp; as part of that, I did a sort on the cases, so that we could update these statements in the future without worrying about the ordering between our internal fork (which we are working on upstreaming), and LLVM's.

--

Patch is 27.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66128.diff

7 Files Affected:

  • (modified) compiler-rt/lib/asan/asan_win_dll_thunk.cpp (+7)
  • (modified) compiler-rt/lib/interception/interception_win.cpp (+71-20)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc (+99)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp (+19)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_libc.h (+2-1)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h (+4-1)
  • (added) compiler-rt/test/asan/TestCases/Windows/replaced_functions.c (+237)
diff --git a/compiler-rt/lib/asan/asan_win_dll_thunk.cpp b/compiler-rt/lib/asan/asan_win_dll_thunk.cpp
index 0fa636bec0d001a..f122fb3145e19f8 100644
--- a/compiler-rt/lib/asan/asan_win_dll_thunk.cpp
+++ b/compiler-rt/lib/asan/asan_win_dll_thunk.cpp
@@ -93,7 +93,14 @@ INTERCEPT_LIBRARY_FUNCTION(strstr);
 INTERCEPT_LIBRARY_FUNCTION(strtok);
 INTERCEPT_LIBRARY_FUNCTION(strtol);
 INTERCEPT_LIBRARY_FUNCTION(strtoll);
+INTERCEPT_LIBRARY_FUNCTION(wcscat);
+INTERCEPT_LIBRARY_FUNCTION(wcschr);
+INTERCEPT_LIBRARY_FUNCTION(wcscmp);
+INTERCEPT_LIBRARY_FUNCTION(wcscpy);
 INTERCEPT_LIBRARY_FUNCTION(wcslen);
+INTERCEPT_LIBRARY_FUNCTION(wcsncat);
+INTERCEPT_LIBRARY_FUNCTION(wcsncmp);
+INTERCEPT_LIBRARY_FUNCTION(wcsncpy);
 INTERCEPT_LIBRARY_FUNCTION(wcsnlen);
 
 #  if defined(_MSC_VER) && !defined(__clang__)
diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp
index 00c317510e42087..3217abe43e860c0 100644
--- a/compiler-rt/lib/interception/interception_win.cpp
+++ b/compiler-rt/lib/interception/interception_win.cpp
@@ -520,15 +520,21 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
   switch (*(u16*)(address)) {
     case 0x018A:  // 8A 01 : mov al, byte ptr [ecx]
     case 0xFF8B:  // 8B FF : mov edi, edi
+    case 0xDC8B:  // 8B DC : mov ebx, esp
     case 0xEC8B:  // 8B EC : mov ebp, esp
     case 0xc889:  // 89 C8 : mov eax, ecx
     case 0xE589:  // 89 E5 : mov ebp, esp
     case 0xC18B:  // 8B C1 : mov eax, ecx
+    case 0xFF33:  // 33 FF : xor edi, edi
     case 0xC033:  // 33 C0 : xor eax, eax
     case 0xC933:  // 33 C9 : xor ecx, ecx
     case 0xD233:  // 33 D2 : xor edx, edx
       return 2;
 
+    case 0xEC83:  // 83 EC XX : sub esp, XX
+    case 0xE483:  // 83 E4 XX : and esp, XX
+      return 3;
+
     // Cannot overwrite control-instruction. Return 0 to indicate failure.
     case 0x25FF:  // FF 25 XX XX XX XX : jmp [XXXXXXXX]
       return 0;
@@ -572,6 +578,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
     case 0x018a:  // mov al, byte ptr [rcx]
       return 2;
 
+    case 0xE483:    // 83 E4 XX : and esp, XX
+    case 0xC1F6:    // F6 C1 XX : test cl, XX
+      return 3;
+
     case 0x058B:  // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
       if (rel_offset)
         *rel_offset = 2;
@@ -579,37 +589,72 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
   }
 
   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 0xc98548:    // 48 85 C9 : test rcx, rcx
-    case 0xd28548:    // 48 85 d2 : test rdx, rdx
-    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 0xc93345:    // 45 33 c9 : xor r9d, r9d
-    case 0xdb3345:    // 45 33 DB : xor r11d, r11d
-    case 0xd98b4c:    // 4c 8b d9 : mov r11, rcx
-    case 0xd28b4c:    // 4c 8b d2 : mov r10, rdx
-    case 0xc98b4c:    // 4C 8B C9 : mov r9, rcx
-    case 0xc18b4c:    // 4C 8B C1 : mov r8, rcx
     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 0x01b70f:    // 0f b7 01 : movzx eax, word ptr [rcx]
+    case 0x02b70f:    // 0f b7 02 : movzx eax, word ptr [rdx]
+    case 0x10b70f:    // 0f b7 10 : movzx edx, word ptr [rax]
+    case 0xc2b70f:    // 0f b7 c2 : movzx eax, dx
     case 0xc00b4d:    // 3d 0b c0 : or r8, r8
     case 0xc08b41:    // 41 8b c0 : mov eax, r8d
+    case 0xc18b41:    // 41 8b c1 : mov eax, r9d
+    case 0xc28b41:    // 41 8b c2 : mov eax, r10d
+    case 0xc38b41:    // 41 8b c3 : mov eax, r11d
+    case 0xc48b41:    // 41 8b c4 : mov eax, r12d
+    case 0xc03345:    // 45 33 c0 : xor r8d, r8d
+    case 0xc93345:    // 45 33 c9 : xor r9d, r9d
+    case 0xdb3345:    // 45 33 db : xor r11d, r11d
+    case 0xca2b48:    // 48 2b ca : sub rcx, rdx
+    case 0xd12b48:    // 48 2b d1 : sub rdx, rcx
+    case 0xca3b48:    // 48 3b ca : cmp rcx, rdx
+    case 0xc08548:    // 48 85 c0 : test rax, rax
+    case 0xc98548:    // 48 85 c9 : test rcx, rcx
+    case 0xd28548:    // 48 85 d2 : test rdx, rdx
+    case 0xdb8548:    // 48 85 db : test rbx, rbx
+    case 0xe48548:    // 48 85 e4 : test rsp, rsp
+    case 0xed8548:    // 48 85 ed : test rbp, rbp
+    case 0xe58948:    // 48 89 e5 : mov rbp, rsp
+    case 0xc18b48:    // 48 8b c1 : mov rax, rcx
+    case 0xc48b48:    // 48 8b c4 : mov rax, rsp
     case 0xd18b48:    // 48 8b d1 : mov rdx, rcx
-    case 0xdc8b4c:    // 4c 8b dc : mov r11, rsp
+    case 0xd9f748:    // 48 f7 d9 : neg rcx
+    case 0xc0ff48:    // 48 ff c0 : inc rax
+    case 0xc1ff48:    // 48 ff c1 : inc rcx
+    case 0xc2ff48:    // 48 ff c2 : inc rdx
+    case 0xc3ff48:    // 48 ff c3 : inc rbx
+    case 0xc6ff48:    // 48 ff c6 : inc rsi
+    case 0xc7ff48:    // 48 ff c7 : inc rdi
+    case 0xc0ff49:    // 49 ff c0 : inc r8
+    case 0xc1ff49:    // 49 ff c1 : inc r9
+    case 0xc2ff49:    // 49 ff c2 : inc r10
+    case 0xc3ff49:    // 49 ff c3 : inc r11
+    case 0xc4ff49:    // 49 ff c4 : inc r12
+    case 0xc5ff49:    // 49 ff c5 : inc r13
+    case 0xc6ff49:    // 49 ff c6 : inc r14
+    case 0xc7ff49:    // 49 ff c7 : inc r15
+    case 0xc22b4c:    // 4c 2b c2 : sub r8, rdx
+    case 0xc18b4c:    // 4c 8b c1 : mov r8, rcx
+    case 0xc98b4c:    // 4c 8b c9 : mov r9, rcx
     case 0xd18b4c:    // 4c 8b d1 : mov r10, rcx
-    case 0xE0E483:    // 83 E4 E0 : and esp, 0xFFFFFFE0
+    case 0xd28b4c:    // 4c 8b d2 : mov r10, rdx
+    case 0xd98b4c:    // 4c 8b d9 : mov r11, rcx
+    case 0xdc8b4c:    // 4c 8b dc : mov r11, rsp
+    case 0xc0854d:    // 4d 85 c0 : test r8, r8
+    case 0xc9854d:    // 4d 85 c9 : test r9, r9
+    case 0xd2854d:    // 4d 85 d2 : test r10, r10
+    case 0xdb854d:    // 4d 85 db : test r11, r11
+    case 0xe4854d:    // 4d 85 e4 : test r12, r12
+    case 0xed854d:    // 4d 85 ed : test r13, r13
+    case 0xf6854d:    // 4d 85 f6 : test r14, r14
+    case 0xff854d:    // 4d 85 ff : test r15, r15
       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
+    case 0x245489:    // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx
+    case 0x398366:    // 66 83 39 XX : cmp DWORD PTR [rcx], XX
+    case 0x428d44:    // 44 8d 42 XX : lea r8d , [rdx + XX]
       return 4;
 
     case 0xec8148:    // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
@@ -631,6 +676,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
   }
 
   switch (*(u32*)(address)) {
+    case 0x01b70f44:  // 44 0f b7 01 : movzx r8d, WORD PTR [rcx]
+      return 4;
     case 0x24448b48:  // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
     case 0x246c8948:  // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
     case 0x245c8948:  // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
@@ -640,6 +687,10 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
     case 0x24548948:  // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
     case 0x244c894c:  // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
     case 0x2444894c:  // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
+    case 0x244c8944:  // 44 89 4c 24 XX   mov DWORD PTR [rsp + XX], r9d
+    case 0x24448944:  // 44 89 44 24 XX   mov DWORD PTR [rsp + XX], r8d
+    case 0x24548966:  // 66 89 54 24 XX : mov [rsp + XX], dx
+    case 0x246c8d48:  // 48 8d 6c 24 XX : lea rbp, [rsp + XX]
       return 5;
     case 0x24648348:  // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
       return 6;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 0e563fa12022a3e..87192ed2d5aa227 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -449,6 +449,9 @@ INTERCEPTOR(char*, textdomain, const char *domainname) {
 static inline int CharCmpX(unsigned char c1, unsigned char c2) {
   return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
 }
+static inline int WCharCmpX(wchar_t c1, wchar_t c2) {
+  return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
+}
 
 DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
                               const char *s1, const char *s2, int result)
@@ -7090,6 +7093,99 @@ INTERCEPTOR(wchar_t *, wcsdup, wchar_t *s) {
 #define INIT_WCSDUP
 #endif
 
+#if SANITIZER_INTERCEPT_WCSCPY
+INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dst, const wchar_t *src) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcscpy, dst, src);
+  SIZE_T src_len = internal_wcslen(src);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(wchar_t) * (src_len + 1));
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(wchar_t) * (src_len + 1));
+  wchar_t *result = internal_wcscpy(dst, src);
+  return result;
+}
+INTERCEPTOR(wchar_t *, wcsncpy, wchar_t *dst, const wchar_t *src, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcsncpy, dst, src, size);
+  SIZE_T src_len = internal_wcsnlen(src, size);
+  if (src_len != size)
+    ++src_len; // account for the nul terminator
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(wchar_t) * src_len);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(wchar_t) * src_len);
+  wchar_t *result = internal_wcsncpy(dst, src, size);
+  return result;
+}
+
+#define INIT_WCSCPY \
+  COMMON_INTERCEPT_FUNCTION(wcscpy); \
+  COMMON_INTERCEPT_FUNCTION(wcsncpy);
+#else
+#define INIT_WCSCPY
+#endif
+
+#if SANITIZER_INTERCEPT_WCSCMP
+INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcscmp, s1, s2);
+  wchar_t c1, c2;
+  uptr i;
+  for (i = 0;; i++) {
+    c1 = s1[i];
+    c2 = s2[i];
+    if (c1 != c2 || c1 == '\0') break;
+  }
+  if (common_flags()->intercept_strcmp) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, sizeof(wchar_t) * (i + 1));
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, sizeof(wchar_t) * (i + 1));
+  }
+  int result = WCharCmpX(c1, c2);
+  return result;
+}
+INTERCEPTOR(int, wcsncmp, wchar_t *s1, const wchar_t *s2, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcsncmp, s1, s2, size);
+  wchar_t c1, c2;
+  uptr i;
+  for (i = 0; i < size; i++) {
+    c1 = s1[i];
+    c2 = s2[i];
+    if (c1 != c2 || c1 == '\0') break;
+  }
+  uptr i1 = i;
+  uptr i2 = i;
+  if (common_flags()->strict_string_checks) {
+    for (; i1 < size && s1[i1]; i1++) {}
+    for (; i2 < size && s2[i2]; i2++) {}
+  }
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, sizeof(wchar_t) * Min(i1 + 1, size));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, sizeof(wchar_t) * Min(i2 + 1, size));
+  int result = WCharCmpX(c1, c2);
+  return result;
+}
+
+#define INIT_WCSCMP \
+  COMMON_INTERCEPT_FUNCTION(wcscmp); \
+  COMMON_INTERCEPT_FUNCTION(wcsncmp);
+#else
+#define INIT_WCSCMP
+#endif
+
+#if SANITIZER_INTERCEPT_WCSCHR
+INTERCEPTOR(wchar_t*, wcschr, const wchar_t *s, wchar_t c) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcschr, s, c);
+  wchar_t *result = REAL(wcschr)(s, c);
+  if (common_flags()->intercept_strchr) {
+    SIZE_T read_size = (result ? result - s : internal_wcslen(s)) + 1;
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s, sizeof(*s) * read_size);
+  }
+  return result;
+}
+
+#define INIT_WCSCHR COMMON_INTERCEPT_FUNCTION(wcschr)
+#else
+#define INIT_WCSCHR
+#endif
+
 #if SANITIZER_INTERCEPT_STRXFRM
 static SIZE_T RealStrLen(const char *str) { return internal_strlen(str); }
 
@@ -10474,6 +10570,9 @@ static void InitializeCommonInterceptors() {
   INIT_WCSLEN;
   INIT_WCSCAT;
   INIT_WCSDUP;
+  INIT_WCSCHR;
+  INIT_WCSCMP;
+  INIT_WCSCPY;
   INIT_WCSXFRM;
   INIT___WCSXFRM_L;
   INIT_ACCT;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp
index 4a6fa5e8dbacb49..5658164500d7934 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp
@@ -279,6 +279,25 @@ uptr internal_wcsnlen(const wchar_t *s, uptr maxlen) {
   return i;
 }
 
+wchar_t* internal_wcscpy(wchar_t* dst, const wchar_t* src) {
+  wchar_t* dst_it = dst;
+  do {
+    *dst_it++ = *src++;
+  } while (*src);
+  return dst;
+}
+
+wchar_t* internal_wcsncpy(wchar_t* dst, const wchar_t* src, uptr maxlen) {
+  uptr i = 0;
+  for (; src[i] && i < maxlen; ++i) {
+    dst[i] = src[i];
+  }
+  for (; i < maxlen; ++i) {
+    dst[i] = 0;
+  }
+  return dst;
+}
+
 bool mem_is_zero(const char *beg, uptr size) {
   CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40));  // Sanity check.
   const char *end = beg + size;
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_libc.h b/compiler-rt/lib/sanitizer_common/sanitizer_libc.h
index e881db2079086d4..5ac547c195383e9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_libc.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_libc.h
@@ -71,7 +71,8 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...)
     FORMAT(3, 4);
 uptr internal_wcslen(const wchar_t *s);
 uptr internal_wcsnlen(const wchar_t *s, uptr maxlen);
-
+wchar_t* internal_wcscpy(wchar_t* dst, const wchar_t* src);
+wchar_t* internal_wcsncpy(wchar_t* dst, const wchar_t* src, uptr maxlen);
 // Return true if all bytes in [mem, mem+size) are zero.
 // Optimized for the case when the result is true.
 bool mem_is_zero(const char *mem, uptr size);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 8c7c00de6d1292f..ef5d14d4e16f967 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -496,8 +496,11 @@
 #define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
 #define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
 #define SANITIZER_INTERCEPT_WCSLEN 1
-#define SANITIZER_INTERCEPT_WCSCAT SI_POSIX
+#define SANITIZER_INTERCEPT_WCSCAT (SI_POSIX || SI_WINDOWS)
 #define SANITIZER_INTERCEPT_WCSDUP SI_POSIX
+#define SANITIZER_INTERCEPT_WCSCPY SI_WINDOWS
+#define SANITIZER_INTERCEPT_WCSCMP SI_WINDOWS
+#define SANITIZER_INTERCEPT_WCSCHR SI_WINDOWS
 #define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA)
 #define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID
 
diff --git a/compiler-rt/test/asan/TestCases/Windows/replaced_functions.c b/compiler-rt/test/asan/TestCases/Windows/replaced_functions.c
new file mode 100644
index 000000000000000..609da83690b3f92
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/Windows/replaced_functions.c
@@ -0,0 +1,237 @@
+// RUN: %clang_cl_asan /Od %s /Fe%t
+
+#define _CRT_SECURE_NO_WARNINGS
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+// the size parameter is the size of the src buffer in bytes
+// (dst has size 2 * size, for strcat reasons)
+// the last two bytes are 0 (for str/wcs functions)
+// they have distinct first bytes
+// dst will be free'd if --fail is passed
+typedef int test_function_t(void *, const void *, size_t);
+
+#if __clang__
+#  define DECLARE_WRAPPED(name) typeof(name) __asan_wrap_##name;
+#else
+// typeof is only supported in MSVC as of 17.7, with `-std:clatest`,
+// and it doesn't seem to be possible to pass an option _only_ to cl
+#  define DECLARE_WRAPPED(name) int __asan_wrap_##name();
+#endif
+
+#define TEST_FUNCTION_DECL(name)                                               \
+  __declspec(dllexport) int test_##name(void *dst, const void *src, size_t size)
+#define TEST_WRAPPED_FUNCTION_DECL(name)                                       \
+  DECLARE_WRAPPED(name)                                                        \
+  __declspec(dllexport) int test_wrap_##name(void *dst, const void *src,       \
+                                             size_t size)
+
+#define TEST_FUNCTION(name, ...)                                               \
+  TEST_FUNCTION_DECL(name) { return 0 != name(__VA_ARGS__); }                  \
+  TEST_WRAPPED_FUNCTION_DECL(name) {                                           \
+    return 0 != __asan_wrap_##name(__VA_ARGS__);                               \
+  }
+
+#define TEST_NOT_FUNCTION(name, ...)                                           \
+  TEST_FUNCTION_DECL(name) { return 0 == name(__VA_ARGS__); }                  \
+  TEST_WRAPPED_FUNCTION_DECL(name) {                                           \
+    return 0 == __asan_wrap_##name(__VA_ARGS__);                               \
+  }
+
+// RUN: %run %t --success memset 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped memset 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail memset 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped memset 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(memset, dst, *(const char *)src, size)
+// RUN: %run %t --success memmove 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped memmove 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail memmove 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped memmove 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(memmove, dst, src, size)
+// RUN: %run %t --success memcpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped memcpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail memcpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped memcpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(memcpy, dst, src, size)
+// RUN: %run %t --success memcmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped memcmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail memcmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped memcmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(memcmp, dst, src, size)
+// RUN: %run %t --success memchr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped memchr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail memchr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped memchr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_NOT_FUNCTION(memchr, dst, *(const char *)src, size)
+
+// RUN: %run %t --success strlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped strlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail strlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped strlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(strlen, dst)
+// RUN: %run %t --success strnlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: %run %t --success --wrapped strnlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
+// RUN: not %run %t --fail strnlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+// RUN: not %run %t --fail --wrapped strnlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL
+TEST_FUNCTION(strnlen, dst, size)
+// RUN: %run %t --success strcpy 2>&1 | FileCheck %s --che...

@@ -279,6 +279,25 @@ uptr internal_wcsnlen(const wchar_t *s, uptr maxlen) {
return i;
}

wchar_t* internal_wcscpy(wchar_t* dst, const wchar_t* src) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather put this next to char* version and make sure implementation looks consistent.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will note that wcslen is also quite far from strlen; we may eventually want to move these together.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move this into a separate patch, feel free to reorder those as well.

return dst;
}

wchar_t* internal_wcsncpy(wchar_t* dst, const wchar_t* src, uptr maxlen) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if you have internal_* related changes in a separate patch, so I can approve them.
I would like to leave Win32 stuff to other folks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can certainly do that, although given the miniscule nature of the changes and the necessity of the latter changes to make those changes make sense, I don't love doing so.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vitalybuka opened this as a PR, #66529

strega-nil-ms pushed a commit to strega-nil/llvm-project that referenced this pull request Sep 15, 2023
These functions are required for the related wcs[n]cpy functions to be
wrapped on Windows, since given our current method of wrapping
functions, calling REAL(wcs[n]cpy) is broken.

@vitalybuka requested that these changes be split out from
llvm#66128.
@strega-nil strega-nil marked this pull request as draft September 15, 2023 16:51
vitalybuka pushed a commit that referenced this pull request Sep 15, 2023
These functions are required for the related wcs[n]cpy functions to be
wrapped on Windows, since given our current method of wrapping
functions, calling REAL(wcs[n]cpy) is broken.

@vitalybuka requested that these changes be split out from
#66128.
wcs[n]cat, wcs[n]cmp, wcs[n]cpy, and wcschr

wcs[n]cat already existed, but only on POSIX
Co-authored-by: alvinhochun <alvin@alvinhc.com>
@strega-nil-ms strega-nil-ms marked this pull request as ready for review September 18, 2023 16:07
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this pull request Sep 19, 2023
These functions are required for the related wcs[n]cpy functions to be
wrapped on Windows, since given our current method of wrapping
functions, calling REAL(wcs[n]cpy) is broken.

@vitalybuka requested that these changes be split out from
llvm#66128.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants