Index: lib/msan/tests/msan_test.cc =================================================================== --- lib/msan/tests/msan_test.cc +++ lib/msan/tests/msan_test.cc @@ -2123,6 +2123,16 @@ EXPECT_EQ(buff[0], 'a'); } +TEST(MemorySanitizer, wctomb) { + wchar_t x = L'a'; + char buff[10]; + wctomb(nullptr, x); + int res = wctomb(buff, x); + EXPECT_EQ(res, 1); + EXPECT_EQ(buff[0], 'a'); + EXPECT_POISONED(buff[1]); +} + TEST(MemorySanitizer, wmemset) { wchar_t x[25]; break_optimization(x); Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -3540,6 +3540,28 @@ #define INIT_WCRTOMB #endif +#if SANITIZER_INTERCEPT_WCTOMB +INTERCEPTOR(int, wctomb, char *dest, wchar_t src) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wctomb, dest, src); + if (!dest) + return REAL(wctomb)(dest, src); + + char *local_dest = (char *)WRAP(malloc)(__sanitizer_MB_LEN_MAX); + int res = REAL(wctomb)(local_dest, src); + if (res != -1) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, res); + REAL(memcpy)(dest, local_dest, res); + WRAP(free)(local_dest); + } + return res; +} + +#define INIT_WCTOMB COMMON_INTERCEPT_FUNCTION(wctomb); +#else +#define INIT_WCTOMB +#endif + #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; @@ -9616,6 +9638,7 @@ INIT_WCSTOMBS; INIT_WCSNRTOMBS; INIT_WCRTOMB; + INIT_WCTOMB; INIT_TCGETATTR; INIT_REALPATH; INIT_CANONICALIZE_FILE_NAME; Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -283,6 +283,9 @@ #define SANITIZER_INTERCEPT_WCRTOMB \ (SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \ SI_SOLARIS) +#define SANITIZER_INTERCEPT_WCTOMB \ + (SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \ + SI_SOLARIS) #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID || SI_SOLARIS #define SANITIZER_INTERCEPT_REALPATH SI_POSIX #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME \ Index: lib/sanitizer_common/sanitizer_platform_limits_posix.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -1428,6 +1428,8 @@ extern const int si_SEGV_MAPERR; extern const int si_SEGV_ACCERR; + + extern const unsigned __sanitizer_MB_LEN_MAX; } // namespace __sanitizer #define CHECK_TYPE_SIZE(TYPE) \ Index: lib/sanitizer_common/sanitizer_platform_limits_posix.cc =================================================================== --- lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -913,6 +913,8 @@ const int si_SEGV_MAPERR = SEGV_MAPERR; const int si_SEGV_ACCERR = SEGV_ACCERR; + + const unsigned __sanitizer_MB_LEN_MAX = MB_LEN_MAX; } // namespace __sanitizer using namespace __sanitizer; Index: test/sanitizer_common/TestCases/wctomb.c =================================================================== --- /dev/null +++ test/sanitizer_common/TestCases/wctomb.c @@ -0,0 +1,14 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include +#include + +int main(int argc, char **argv) { + char buff[10]; + wchar_t x = L'a'; + wctomb(NULL, x); + int res = wctomb(buff, x); + assert(res == 1); + assert(buff[0] == 'a'); + return 0; +}