[compiler-rt] [compiler-rt][asan] Reland: wcscpy/wcsncpy interceptors and stabilize wchar tests on Darwin/Android (PR #162028)

Yixuan Cao via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 5 16:55:44 PDT 2025


https://github.com/Cao-Wuhui updated https://github.com/llvm/llvm-project/pull/162028

>From 0ef67e77cdf42123beefc21cd796315b0e99981a Mon Sep 17 00:00:00 2001
From: Yixuan Cao <caoyixuan2019 at email.szu.edu.cn>
Date: Mon, 6 Oct 2025 06:49:09 +0800
Subject: [PATCH 1/4] [compiler-rt][asan] Reland: Add wcscpy/wcsncpy; enable
 wcscat/wcsncat on Windows (#160493)

---
 compiler-rt/lib/asan/asan_interceptors.cpp    | 46 +++++++++++++++++--
 compiler-rt/lib/asan/asan_interceptors.h      |  1 +
 .../asan/asan_win_static_runtime_thunk.cpp    |  4 ++
 .../sanitizer_platform_interceptors.h         |  2 +-
 compiler-rt/test/asan/TestCases/wcscat.cpp    | 26 +++++++++++
 compiler-rt/test/asan/TestCases/wcscpy.cpp    | 23 ++++++++++
 compiler-rt/test/asan/TestCases/wcsncat.cpp   | 27 +++++++++++
 compiler-rt/test/asan/TestCases/wcsncpy.cpp   | 25 ++++++++++
 8 files changed, 150 insertions(+), 4 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/wcscat.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/wcscpy.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/wcsncat.cpp
 create mode 100644 compiler-rt/test/asan/TestCases/wcsncpy.cpp

diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 7c9a08b9083a2..0f613f0fdc30b 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -58,13 +58,20 @@ namespace __asan {
 
 static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
 #if SANITIZER_INTERCEPT_STRNLEN
-  if (REAL(strnlen)) {
+  if (REAL(strnlen))
     return REAL(strnlen)(s, maxlen);
-  }
-#endif
+#  endif
   return internal_strnlen(s, maxlen);
 }
 
+static inline uptr MaybeRealWcsnlen(const wchar_t* s, uptr maxlen) {
+#  if SANITIZER_INTERCEPT_WCSNLEN
+  if (REAL(wcsnlen))
+    return REAL(wcsnlen)(s, maxlen);
+#  endif
+  return internal_wcsnlen(s, maxlen);
+}
+
 void SetThreadName(const char *name) {
   AsanThread *t = GetCurrentThread();
   if (t)
@@ -570,6 +577,20 @@ INTERCEPTOR(char *, strcpy, char *to, const char *from) {
   return REAL(strcpy)(to, from);
 }
 
+INTERCEPTOR(wchar_t*, wcscpy, wchar_t* to, const wchar_t* from) {
+  void* ctx;
+  ASAN_INTERCEPTOR_ENTER(ctx, wcscpy);
+  if (!TryAsanInitFromRtl())
+    return REAL(wcscpy)(to, from);
+  if (flags()->replace_str) {
+    uptr size = (internal_wcslen(from) + 1) * sizeof(wchar_t);
+    CHECK_RANGES_OVERLAP("wcscpy", to, size, from, size);
+    ASAN_READ_RANGE(ctx, from, size);
+    ASAN_WRITE_RANGE(ctx, to, size);
+  }
+  return REAL(wcscpy)(to, from);
+}
+
 // Windows doesn't always define the strdup identifier,
 // and when it does it's a macro defined to either _strdup
 // or _strdup_dbg, _strdup_dbg ends up calling _strdup, so
@@ -633,6 +654,20 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, usize size) {
   return REAL(strncpy)(to, from, size);
 }
 
+INTERCEPTOR(wchar_t*, wcsncpy, wchar_t* to, const wchar_t* from, uptr size) {
+  void* ctx;
+  ASAN_INTERCEPTOR_ENTER(ctx, wcsncpy);
+  AsanInitFromRtl();
+  if (flags()->replace_str) {
+    uptr from_size =
+        Min(size, MaybeRealWcsnlen(from, size) + 1) * sizeof(wchar_t);
+    CHECK_RANGES_OVERLAP("wcsncpy", to, from_size, from, from_size);
+    ASAN_READ_RANGE(ctx, from, from_size);
+    ASAN_WRITE_RANGE(ctx, to, size * sizeof(wchar_t));
+  }
+  return REAL(wcsncpy)(to, from, size);
+}
+
 template <typename Fn>
 static ALWAYS_INLINE auto StrtolImpl(void *ctx, Fn real, const char *nptr,
                                      char **endptr, int base)
@@ -809,6 +844,11 @@ void InitializeAsanInterceptors() {
   ASAN_INTERCEPT_FUNC(strncat);
   ASAN_INTERCEPT_FUNC(strncpy);
   ASAN_INTERCEPT_FUNC(strdup);
+
+  // Intercept wcs* functions.
+  ASAN_INTERCEPT_FUNC(wcscpy);
+  ASAN_INTERCEPT_FUNC(wcsncpy);
+
 #  if ASAN_INTERCEPT___STRDUP
   ASAN_INTERCEPT_FUNC(__strdup);
 #endif
diff --git a/compiler-rt/lib/asan/asan_interceptors.h b/compiler-rt/lib/asan/asan_interceptors.h
index 3e2386eaf8092..2d551cfafd1f5 100644
--- a/compiler-rt/lib/asan/asan_interceptors.h
+++ b/compiler-rt/lib/asan/asan_interceptors.h
@@ -129,6 +129,7 @@ DECLARE_REAL(char*, strchr, const char *str, int c)
 DECLARE_REAL(SIZE_T, strlen, const char *s)
 DECLARE_REAL(char*, strncpy, char *to, const char *from, SIZE_T size)
 DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
+DECLARE_REAL(SIZE_T, wcsnlen, const wchar_t* s, SIZE_T maxlen)
 DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
 
 #  if !SANITIZER_APPLE
diff --git a/compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp b/compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp
index 4a69b66574039..46e0e90738f24 100644
--- a/compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp
+++ b/compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp
@@ -63,6 +63,10 @@ INTERCEPT_LIBRARY_FUNCTION_ASAN(strpbrk);
 INTERCEPT_LIBRARY_FUNCTION_ASAN(strspn);
 INTERCEPT_LIBRARY_FUNCTION_ASAN(strstr);
 INTERCEPT_LIBRARY_FUNCTION_ASAN(strtok);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcscat);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcscpy);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsncat);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsncpy);
 INTERCEPT_LIBRARY_FUNCTION_ASAN(wcslen);
 INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsnlen);
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 29987decdff45..88ecd7e16306a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -551,7 +551,7 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
 #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_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA)
 #define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID
diff --git a/compiler-rt/test/asan/TestCases/wcscat.cpp b/compiler-rt/test/asan/TestCases/wcscat.cpp
new file mode 100644
index 0000000000000..dcdff88c18ef1
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/wcscat.cpp
@@ -0,0 +1,26 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+
+#include <stdio.h>
+#include <wchar.h>
+
+int main() {
+  wchar_t *start = L"X means ";
+  wchar_t *append = L"dog";
+  wchar_t goodDst[12];
+  wcscpy(goodDst, start);
+  wcscat(goodDst, append);
+
+  wchar_t badDst[9];
+  wcscpy(badDst, start);
+  printf("Good so far.\n");
+  // CHECK: Good so far.
+  fflush(stdout);
+  wcscat(badDst, append); // Boom!
+  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
+  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
+  printf("Should have failed with ASAN error.\n");
+}
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcscpy.cpp b/compiler-rt/test/asan/TestCases/wcscpy.cpp
new file mode 100644
index 0000000000000..414d83303a960
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/wcscpy.cpp
@@ -0,0 +1,23 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+
+#include <stdio.h>
+#include <wchar.h>
+
+int main() {
+  wchar_t *src = L"X means dog";
+  wchar_t goodDst[12];
+  wcscpy(goodDst, src);
+
+  wchar_t badDst[7];
+  printf("Good so far.\n");
+  // CHECK: Good so far.
+  fflush(stdout);
+  wcscpy(badDst, src); // Boom!
+  // CHECK:ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
+  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
+  printf("Should have failed with ASAN error.\n");
+}
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncat.cpp b/compiler-rt/test/asan/TestCases/wcsncat.cpp
new file mode 100644
index 0000000000000..3ab7fc8f55d63
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/wcsncat.cpp
@@ -0,0 +1,27 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+
+#include <stdio.h>
+#include <wchar.h>
+
+int main() {
+  wchar_t *start = L"X means ";
+  wchar_t *append = L"dog";
+  wchar_t goodDst[15];
+  wcscpy(goodDst, start);
+  wcsncat(goodDst, append, 5);
+
+  wchar_t badDst[11];
+  wcscpy(badDst, start);
+  wcsncat(badDst, append, 1);
+  printf("Good so far.\n");
+  // CHECK: Good so far.
+  fflush(stdout);
+  wcsncat(badDst, append, 3); // Boom!
+  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
+  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
+  printf("Should have failed with ASAN error.\n");
+}
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncpy.cpp b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
new file mode 100644
index 0000000000000..6177b72990a0a
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
@@ -0,0 +1,25 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+
+#include <stdio.h>
+#include <wchar.h>
+
+int main() {
+  wchar_t *src = L"X means dog";
+  wchar_t goodDst[12];
+  wcsncpy(goodDst, src, 12);
+
+  wchar_t badDst[7];
+  wcsncpy(badDst, src, 7); // This should still work.
+  printf("Good so far.\n");
+  // CHECK: Good so far.
+  fflush(stdout);
+
+  wcsncpy(badDst, src, 15); // Boom!
+  // CHECK:ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
+  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
+  printf("Should have failed with ASAN error.\n");
+}
\ No newline at end of file

>From 373cca4d8546f9239efe084ca74e8bdf3156478c Mon Sep 17 00:00:00 2001
From: Yixuan Cao <caoyixuan2019 at email.szu.edu.cn>
Date: Mon, 6 Oct 2025 06:49:34 +0800
Subject: [PATCH 2/4] [compiler-rt][asan][tests] Reland: Stabilize wchar tests
 on Darwin/Android (#161624)

---
 compiler-rt/test/asan/TestCases/wcscat.cpp  | 20 ++++++++++----------
 compiler-rt/test/asan/TestCases/wcscpy.cpp  | 20 ++++++++++----------
 compiler-rt/test/asan/TestCases/wcsncat.cpp | 20 ++++++++++----------
 compiler-rt/test/asan/TestCases/wcsncpy.cpp | 20 ++++++++++----------
 4 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/wcscat.cpp b/compiler-rt/test/asan/TestCases/wcscat.cpp
index dcdff88c18ef1..f0a8ec12580b3 100644
--- a/compiler-rt/test/asan/TestCases/wcscat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscat.cpp
@@ -1,26 +1,26 @@
-// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O0 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
 
 #include <stdio.h>
 #include <wchar.h>
 
 int main() {
-  wchar_t *start = L"X means ";
-  wchar_t *append = L"dog";
+  const wchar_t *start = L"X means ";
+  const wchar_t *append = L"dog";
   wchar_t goodDst[12];
   wcscpy(goodDst, start);
   wcscat(goodDst, append);
 
   wchar_t badDst[9];
   wcscpy(badDst, start);
-  printf("Good so far.\n");
+  fprintf(stderr, "Good so far.\n");
   // CHECK: Good so far.
-  fflush(stdout);
+  fflush(stderr);
   wcscat(badDst, append); // Boom!
   // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
-  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK: #0 {{0x[0-9a-f]+}} in wcscat
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcscpy.cpp b/compiler-rt/test/asan/TestCases/wcscpy.cpp
index 414d83303a960..a280d29289e37 100644
--- a/compiler-rt/test/asan/TestCases/wcscpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscpy.cpp
@@ -1,23 +1,23 @@
-// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O0 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
 
 #include <stdio.h>
 #include <wchar.h>
 
 int main() {
-  wchar_t *src = L"X means dog";
+  const wchar_t *src = L"X means dog";
   wchar_t goodDst[12];
   wcscpy(goodDst, src);
 
   wchar_t badDst[7];
-  printf("Good so far.\n");
+  fprintf(stderr, "Good so far.\n");
   // CHECK: Good so far.
-  fflush(stdout);
+  fflush(stderr);
   wcscpy(badDst, src); // Boom!
-  // CHECK:ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
-  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
+  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK: #0 {{0x[0-9a-f]+}} in wcscpy
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncat.cpp b/compiler-rt/test/asan/TestCases/wcsncat.cpp
index 3ab7fc8f55d63..eb7d095e45c7a 100644
--- a/compiler-rt/test/asan/TestCases/wcsncat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncat.cpp
@@ -1,14 +1,14 @@
-// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O0 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
 
 #include <stdio.h>
 #include <wchar.h>
 
 int main() {
-  wchar_t *start = L"X means ";
-  wchar_t *append = L"dog";
+  const wchar_t *start = L"X means ";
+  const wchar_t *append = L"dog";
   wchar_t goodDst[15];
   wcscpy(goodDst, start);
   wcsncat(goodDst, append, 5);
@@ -16,12 +16,12 @@ int main() {
   wchar_t badDst[11];
   wcscpy(badDst, start);
   wcsncat(badDst, append, 1);
-  printf("Good so far.\n");
+  fprintf(stderr, "Good so far.\n");
   // CHECK: Good so far.
-  fflush(stdout);
+  fflush(stderr);
   wcsncat(badDst, append, 3); // Boom!
   // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
-  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK: #0 {{0x[0-9a-f]+}} in wcsncat
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncpy.cpp b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
index 6177b72990a0a..1106bf5d264e5 100644
--- a/compiler-rt/test/asan/TestCases/wcsncpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
@@ -1,25 +1,25 @@
-// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
-// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O0 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O1 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O2 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
+// RUN: %clangxx_asan -O3 %s -o %t && not %env_asan_opts=log_to_stderr=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
 
 #include <stdio.h>
 #include <wchar.h>
 
 int main() {
-  wchar_t *src = L"X means dog";
+  const wchar_t *src = L"X means dog";
   wchar_t goodDst[12];
   wcsncpy(goodDst, src, 12);
 
   wchar_t badDst[7];
   wcsncpy(badDst, src, 7); // This should still work.
-  printf("Good so far.\n");
+  fprintf(stderr, "Good so far.\n");
   // CHECK: Good so far.
-  fflush(stdout);
+  fflush(stderr);
 
   wcsncpy(badDst, src, 15); // Boom!
-  // CHECK:ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
-  // CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
+  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK: #0 {{0x[0-9a-f]+}} in wcsncpy
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file

>From 304e76e6b60995d366e8efd597a53df8360a8ef6 Mon Sep 17 00:00:00 2001
From: Yixuan Cao <caoyixuan2019 at email.szu.edu.cn>
Date: Mon, 6 Oct 2025 06:51:47 +0800
Subject: [PATCH 3/4] Darwin: tolerate reordering using DAG group

---
 compiler-rt/test/asan/TestCases/wcscat.cpp  | 8 ++++----
 compiler-rt/test/asan/TestCases/wcscpy.cpp  | 8 ++++----
 compiler-rt/test/asan/TestCases/wcsncat.cpp | 8 ++++----
 compiler-rt/test/asan/TestCases/wcsncpy.cpp | 9 ++++-----
 4 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/wcscat.cpp b/compiler-rt/test/asan/TestCases/wcscat.cpp
index f0a8ec12580b3..833dee9655264 100644
--- a/compiler-rt/test/asan/TestCases/wcscat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscat.cpp
@@ -16,11 +16,11 @@ int main() {
   wchar_t badDst[9];
   wcscpy(badDst, start);
   fprintf(stderr, "Good so far.\n");
-  // CHECK: Good so far.
+  // CHECK-DAG: Good so far.
   fflush(stderr);
   wcscat(badDst, append); // Boom!
-  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
-  // CHECK: #0 {{0x[0-9a-f]+}} in wcscat
+  // CHECK-DAG: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcscat
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcscpy.cpp b/compiler-rt/test/asan/TestCases/wcscpy.cpp
index a280d29289e37..c7205e486e3ff 100644
--- a/compiler-rt/test/asan/TestCases/wcscpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscpy.cpp
@@ -13,11 +13,11 @@ int main() {
 
   wchar_t badDst[7];
   fprintf(stderr, "Good so far.\n");
-  // CHECK: Good so far.
+  // CHECK-DAG: Good so far.
   fflush(stderr);
   wcscpy(badDst, src); // Boom!
-  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
-  // CHECK: #0 {{0x[0-9a-f]+}} in wcscpy
+  // CHECK-DAG: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcscpy
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncat.cpp b/compiler-rt/test/asan/TestCases/wcsncat.cpp
index eb7d095e45c7a..8fe1e510a26cf 100644
--- a/compiler-rt/test/asan/TestCases/wcsncat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncat.cpp
@@ -17,11 +17,11 @@ int main() {
   wcscpy(badDst, start);
   wcsncat(badDst, append, 1);
   fprintf(stderr, "Good so far.\n");
-  // CHECK: Good so far.
+  // CHECK-DAG: Good so far.
   fflush(stderr);
   wcsncat(badDst, append, 3); // Boom!
-  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
-  // CHECK: #0 {{0x[0-9a-f]+}} in wcsncat
+  // CHECK-DAG: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcsncat
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file
diff --git a/compiler-rt/test/asan/TestCases/wcsncpy.cpp b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
index 1106bf5d264e5..7db3351cdb6a6 100644
--- a/compiler-rt/test/asan/TestCases/wcsncpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
@@ -14,12 +14,11 @@ int main() {
   wchar_t badDst[7];
   wcsncpy(badDst, src, 7); // This should still work.
   fprintf(stderr, "Good so far.\n");
-  // CHECK: Good so far.
+  // CHECK-DAG: Good so far.
   fflush(stderr);
-
   wcsncpy(badDst, src, 15); // Boom!
-  // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
-  // CHECK: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
-  // CHECK: #0 {{0x[0-9a-f]+}} in wcsncpy
+  // CHECK-DAG: ERROR: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] at pc {{0x[0-9a-f]+}} bp {{0x[0-9a-f]+}} sp {{0x[0-9a-f]+}}
+  // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
+  // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcsncpy
   printf("Should have failed with ASAN error.\n");
 }
\ No newline at end of file

>From aabcf185b9c2c0e59ed1079b60785b5b806e89af Mon Sep 17 00:00:00 2001
From: Yixuan Cao <caoyixuan2019 at email.szu.edu.cn>
Date: Mon, 6 Oct 2025 07:52:21 +0800
Subject: [PATCH 4/4] Fix: add trailing newlines to wcs*.cpp

Signed-off-by: Yixuan Cao <caoyixuan2019 at email.szu.edu.cn>
---
 compiler-rt/test/asan/TestCases/wcscat.cpp  | 2 +-
 compiler-rt/test/asan/TestCases/wcscpy.cpp  | 2 +-
 compiler-rt/test/asan/TestCases/wcsncat.cpp | 2 +-
 compiler-rt/test/asan/TestCases/wcsncpy.cpp | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/test/asan/TestCases/wcscat.cpp b/compiler-rt/test/asan/TestCases/wcscat.cpp
index 833dee9655264..fd0b5a4310351 100644
--- a/compiler-rt/test/asan/TestCases/wcscat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscat.cpp
@@ -23,4 +23,4 @@ int main() {
   // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
   // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcscat
   printf("Should have failed with ASAN error.\n");
-}
\ No newline at end of file
+}
diff --git a/compiler-rt/test/asan/TestCases/wcscpy.cpp b/compiler-rt/test/asan/TestCases/wcscpy.cpp
index c7205e486e3ff..8133a588cb071 100644
--- a/compiler-rt/test/asan/TestCases/wcscpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcscpy.cpp
@@ -20,4 +20,4 @@ int main() {
   // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
   // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcscpy
   printf("Should have failed with ASAN error.\n");
-}
\ No newline at end of file
+}
diff --git a/compiler-rt/test/asan/TestCases/wcsncat.cpp b/compiler-rt/test/asan/TestCases/wcsncat.cpp
index 8fe1e510a26cf..365e732e2d051 100644
--- a/compiler-rt/test/asan/TestCases/wcsncat.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncat.cpp
@@ -24,4 +24,4 @@ int main() {
   // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
   // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcsncat
   printf("Should have failed with ASAN error.\n");
-}
\ No newline at end of file
+}
diff --git a/compiler-rt/test/asan/TestCases/wcsncpy.cpp b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
index 7db3351cdb6a6..485ddc4804dcd 100644
--- a/compiler-rt/test/asan/TestCases/wcsncpy.cpp
+++ b/compiler-rt/test/asan/TestCases/wcsncpy.cpp
@@ -21,4 +21,4 @@ int main() {
   // CHECK-DAG: WRITE of size {{[0-9]+}} at [[ADDR]] thread T0
   // CHECK-DAG: #0 {{0x[0-9a-f]+}} in wcsncpy
   printf("Should have failed with ASAN error.\n");
-}
\ No newline at end of file
+}



More information about the llvm-commits mailing list