diff --git a/compiler-rt/lib/interception/interception_win.cpp b/compiler-rt/lib/interception/interception_win.cpp --- a/compiler-rt/lib/interception/interception_win.cpp +++ b/compiler-rt/lib/interception/interception_win.cpp @@ -520,15 +520,21 @@ 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; @@ -589,6 +595,9 @@ 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 0xc2b70f: // 0f b7 c2 : movzx eax, dx + case 0x01b70f: // 0f b7 01 : movzx eax, WORD PTR [rcx] + case 0x02b70f: // 0f b7 02 : movzx eax, WORD PTR [rdx] case 0xc03345: // 45 33 c0 : xor r8d, r8d case 0xc93345: // 45 33 c9 : xor r9d, r9d case 0xdb3345: // 45 33 DB : xor r11d, r11d @@ -602,11 +611,13 @@ case 0xc00b4d: // 3d 0b c0 : or r8, r8 case 0xc08b41: // 41 8b c0 : mov eax, r8d case 0xd18b48: // 48 8b d1 : mov rdx, rcx + case 0xc22b4c: // 4c 2b c2 : sub r8, rdx case 0xdc8b4c: // 4c 8b dc : mov r11, rsp case 0xd18b4c: // 4c 8b d1 : mov r10, rcx case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0 return 3; + case 0x398366: // 66 83 39 XX : cmp DWORD PTR [rcx], XX 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 @@ -631,6 +642,8 @@ } 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 +653,7 @@ 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 0x24548966: // 66 89 54 24 XX : mov WORD PTR [rsp + XX], dx 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 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -449,6 +449,9 @@ 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) @@ -7050,6 +7053,103 @@ #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 = REAL(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 = REAL(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); } @@ -10433,6 +10533,9 @@ 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_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h --- 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 --- /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 --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strcpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strcpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strcpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strcpy, dst, src) +// RUN: %run %t --success strncpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strncpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strncpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strncpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strncpy, dst, src, size) +// RUN: %run %t --success strcat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strcat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strcat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strcat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strcat, dst, src) +// RUN: %run %t --success strncat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strncat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strncat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strncat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strncat, dst, src, size) +// RUN: %run %t --success strcmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strcmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strcmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strcmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strcmp, dst, src) +// RUN: %run %t --success strncmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strncmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strncmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strncmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(strncmp, dst, src, size) +// RUN: %run %t --success strchr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped strchr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail strchr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped strchr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_NOT_FUNCTION(strchr, dst, *(const char *)src) + +// RUN: %run %t --success wcslen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcslen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcslen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcslen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcslen, dst) +// RUN: %run %t --success wcsnlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcsnlen 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcsnlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcsnlen 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcsnlen, dst, size) +// RUN: %run %t --success wcscpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcscpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcscpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcscpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcscpy, dst, src) +// RUN: %run %t --success wcsncpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcsncpy 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcsncpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcsncpy 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcsncpy, dst, src, size / 2) +// RUN: %run %t --success wcscat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcscat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcscat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcscat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcscat, dst, src) +// RUN: %run %t --success wcsncat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcsncat 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcsncat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcsncat 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcsncat, dst, src, size / 2) +// RUN: %run %t --success wcscmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcscmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcscmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcscmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcscmp, dst, src) +// note: clang does not actually emit a call to wcsncmp, for some reason +// RUN: %run %t --success --wrapped wcsncmp 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail --wrapped wcsncmp 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_FUNCTION(wcsncmp, dst, src, size / 2) +// RUN: %run %t --success wcschr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: %run %t --success --wrapped wcschr 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS +// RUN: not %run %t --fail wcschr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +// RUN: not %run %t --fail --wrapped wcschr 2>&1 | FileCheck %s --check-prefix=CHECK-FAIL +TEST_NOT_FUNCTION(wcschr, dst, *(const wchar_t *)src) + +void help(const char *progname) { + fprintf(stderr, "Usage: %s (--fail|--success) [--wrapped] \n", + progname); + exit(1); +} + +enum SuccessOrFail { + SOF_None, + SOF_Succeed, + SOF_Fail, +}; + +int main(int argc, char *argv[]) { + if (argc < 3 || argc > 4) { + help(argv[0]); + } + + const size_t size = 8; + + void *src = malloc(size); + memset(src, 1, size); + ((wchar_t *)src)[size / 2 - 1] = L'\0'; + + void *dst = malloc(size * 2); + memset(dst, 2, size); + ((wchar_t *)dst)[size / 2 - 1] = L'\0'; + + bool wrapped = false; + enum SuccessOrFail succeed = SOF_None; + const char *function_name = 0; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--success") == 0) { + succeed = SOF_Succeed; + } else if (strcmp(argv[i], "--fail") == 0) { + succeed = SOF_Fail; + } else if (strcmp(argv[i], "--wrapped") == 0) { + wrapped = true; + } else { + function_name = argv[i]; + } + } + + if (!function_name || succeed == SOF_None) { + help(argv[0]); + } else if (succeed == SOF_Fail) { + free( + dst); // free dst, so that the function _should_ fail if ASan is correctly implemented + } + + test_function_t *test_function = 0; + char buffer[32]; + if (!wrapped) { + strcpy(buffer, "test_"); + } else { + strcpy(buffer, "test_wrap_"); + } + + // CHECK-FAIL: ERROR: AddressSanitizer: heap-use-after-free + // CHECK-SUCCESS: pass + // CHECK-SUCCESS-NOT: ERROR: AddressSanitizer: heap-use-after-free + + if (strncat(buffer, function_name, 32)) { + test_function = (test_function_t *)GetProcAddress(0, buffer); + } + + if (!test_function) { + fprintf(stderr, "Unknown test: %s\n", argv[2]); + return 1; + } + + return !(test_function(dst, src, size) && printf("pass\n")); +}