Skip to content

Commit 5c6dda8

Browse files
committed
[compiler-rt][asan] Add wcscpy/wcsncpy; enable wcscat/wcsncat on Windows
- Implement wchar interceptors; register Windows thunk. - wcsncpy: compute write size in bytes (size * sizeof(wchar_t)) to avoid missed overflows when sizeof(wchar_t) != 1. - Harden tests (fflush, resilient FileCheck). Follow-up to PR #90909: builds on that work and addresses review feedback. Refs: #90909
1 parent 5031c16 commit 5c6dda8

File tree

8 files changed

+149
-1
lines changed

8 files changed

+149
-1
lines changed

compiler-rt/lib/asan/asan_interceptors.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
6565
return internal_strnlen(s, maxlen);
6666
}
6767

68+
static inline uptr MaybeRealWcsnlen(const wchar_t *s, uptr maxlen) {
69+
#if SANITIZER_INTERCEPT_WCSNLEN
70+
if (REAL(wcsnlen)) {
71+
return REAL(wcsnlen)(s, maxlen);
72+
}
73+
#endif
74+
return internal_wcsnlen(s, maxlen);
75+
}
76+
6877
void SetThreadName(const char *name) {
6978
AsanThread *t = GetCurrentThread();
7079
if (t)
@@ -570,6 +579,20 @@ INTERCEPTOR(char *, strcpy, char *to, const char *from) {
570579
return REAL(strcpy)(to, from);
571580
}
572581

582+
INTERCEPTOR(wchar_t *, wcscpy, wchar_t *to, const wchar_t *from) {
583+
void *ctx;
584+
ASAN_INTERCEPTOR_ENTER(ctx, wcscpy);
585+
if (!TryAsanInitFromRtl())
586+
return REAL(wcscpy)(to, from);
587+
if (flags()->replace_str) {
588+
uptr from_size = (internal_wcslen(from) + 1) * sizeof(wchar_t);
589+
CHECK_RANGES_OVERLAP("wcscpy", to, from_size, from, from_size);
590+
ASAN_READ_RANGE(ctx, from, from_size);
591+
ASAN_WRITE_RANGE(ctx, to, from_size);
592+
}
593+
return REAL(wcscpy)(to, from);
594+
}
595+
573596
// Windows doesn't always define the strdup identifier,
574597
// and when it does it's a macro defined to either _strdup
575598
// or _strdup_dbg, _strdup_dbg ends up calling _strdup, so
@@ -633,6 +656,20 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, usize size) {
633656
return REAL(strncpy)(to, from, size);
634657
}
635658

659+
INTERCEPTOR(wchar_t *, wcsncpy, wchar_t *to, const wchar_t *from, uptr size) {
660+
void *ctx;
661+
ASAN_INTERCEPTOR_ENTER(ctx, wcsncpy);
662+
AsanInitFromRtl();
663+
if (flags()->replace_str) {
664+
uptr from_size =
665+
Min(size, MaybeRealWcsnlen(from, size) + 1) * sizeof(wchar_t);
666+
CHECK_RANGES_OVERLAP("wcsncpy", to, from_size, from, from_size);
667+
ASAN_READ_RANGE(ctx, from, from_size);
668+
ASAN_WRITE_RANGE(ctx, to, size * sizeof(wchar_t));
669+
}
670+
return REAL(wcsncpy)(to, from, size);
671+
}
672+
636673
template <typename Fn>
637674
static ALWAYS_INLINE auto StrtolImpl(void *ctx, Fn real, const char *nptr,
638675
char **endptr, int base)
@@ -809,6 +846,11 @@ void InitializeAsanInterceptors() {
809846
ASAN_INTERCEPT_FUNC(strncat);
810847
ASAN_INTERCEPT_FUNC(strncpy);
811848
ASAN_INTERCEPT_FUNC(strdup);
849+
850+
// Intercept wcs* functions.
851+
ASAN_INTERCEPT_FUNC(wcscpy);
852+
ASAN_INTERCEPT_FUNC(wcsncpy);
853+
812854
# if ASAN_INTERCEPT___STRDUP
813855
ASAN_INTERCEPT_FUNC(__strdup);
814856
#endif

compiler-rt/lib/asan/asan_interceptors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ DECLARE_REAL(char*, strchr, const char *str, int c)
129129
DECLARE_REAL(SIZE_T, strlen, const char *s)
130130
DECLARE_REAL(char*, strncpy, char *to, const char *from, SIZE_T size)
131131
DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
132+
DECLARE_REAL(SIZE_T, wcsnlen, const wchar_t *s, SIZE_T maxlen)
132133
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
133134

134135
# if !SANITIZER_APPLE

compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ INTERCEPT_LIBRARY_FUNCTION_ASAN(strpbrk);
6363
INTERCEPT_LIBRARY_FUNCTION_ASAN(strspn);
6464
INTERCEPT_LIBRARY_FUNCTION_ASAN(strstr);
6565
INTERCEPT_LIBRARY_FUNCTION_ASAN(strtok);
66+
INTERCEPT_LIBRARY_FUNCTION(wcscat);
67+
INTERCEPT_LIBRARY_FUNCTION(wcscpy);
68+
INTERCEPT_LIBRARY_FUNCTION(wcsncat);
69+
INTERCEPT_LIBRARY_FUNCTION(wcsncpy);
6670
INTERCEPT_LIBRARY_FUNCTION_ASAN(wcslen);
6771
INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsnlen);
6872

compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
551551
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
552552
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
553553
#define SANITIZER_INTERCEPT_WCSLEN 1
554-
#define SANITIZER_INTERCEPT_WCSCAT SI_POSIX
554+
#define SANITIZER_INTERCEPT_WCSCAT 1
555555
#define SANITIZER_INTERCEPT_WCSDUP SI_POSIX
556556
#define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA)
557557
#define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
2+
// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
3+
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
4+
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
5+
6+
#include <stdio.h>
7+
#include <wchar.h>
8+
9+
int main() {
10+
wchar_t *start = L"X means ";
11+
wchar_t *append = L"dog";
12+
wchar_t goodDst[12];
13+
wcscpy(goodDst, start);
14+
wcscat(goodDst, append);
15+
16+
wchar_t badDst[9];
17+
wcscpy(badDst, start);
18+
printf("Good so far.\n");
19+
// CHECK: Good so far.
20+
fflush(stdout);
21+
wcscat(badDst, append); // Boom!
22+
// 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]+}}
23+
// CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
24+
// CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
25+
printf("Should have failed with ASAN error.\n");
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
2+
// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
3+
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
4+
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
5+
6+
#include <stdio.h>
7+
#include <wchar.h>
8+
9+
int main() {
10+
wchar_t *src = L"X means dog";
11+
wchar_t goodDst[12];
12+
wcscpy(goodDst, src);
13+
14+
wchar_t badDst[7];
15+
printf("Good so far.\n");
16+
// CHECK: Good so far.
17+
fflush(stdout);
18+
wcscpy(badDst, src); // Boom!
19+
// 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]+}}
20+
// CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
21+
// CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcscpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
22+
printf("Should have failed with ASAN error.\n");
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
2+
// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
3+
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
4+
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
5+
6+
#include <stdio.h>
7+
#include <wchar.h>
8+
9+
int main() {
10+
wchar_t *start = L"X means ";
11+
wchar_t *append = L"dog";
12+
wchar_t goodDst[15];
13+
wcscpy(goodDst, start);
14+
wcsncat(goodDst, append, 5);
15+
16+
wchar_t badDst[11];
17+
wcscpy(badDst, start);
18+
wcsncat(badDst, append, 1);
19+
printf("Good so far.\n");
20+
// CHECK: Good so far.
21+
fflush(stdout);
22+
wcsncat(badDst, append, 3); // Boom!
23+
// 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]+}}
24+
// CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
25+
// CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncat{{.*}}sanitizer_common_interceptors.inc:{{[0-9]+}}
26+
printf("Should have failed with ASAN error.\n");
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
2+
// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
3+
// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
4+
// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK
5+
6+
#include <stdio.h>
7+
#include <wchar.h>
8+
9+
int main() {
10+
wchar_t *src = L"X means dog";
11+
wchar_t goodDst[12];
12+
wcsncpy(goodDst, src, 12);
13+
14+
wchar_t badDst[7];
15+
wcsncpy(badDst, src, 7); // This should still work.
16+
printf("Good so far.\n");
17+
// CHECK: Good so far.
18+
fflush(stdout);
19+
20+
wcsncpy(badDst, src, 15); // Boom!
21+
// 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]+}}
22+
// CHECK: WRITE of size {{[0-9]+}} at [[ADDR:0x[0-9a-f]+]] thread T0
23+
// CHECK: #0 [[ADDR:0x[0-9a-f]+]] in wcsncpy{{.*}}asan_interceptors.cpp:{{[0-9]+}}
24+
printf("Should have failed with ASAN error.\n");
25+
}

0 commit comments

Comments
 (0)