Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -3524,13 +3524,16 @@ void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); - // FIXME: under ASan the call below may write to freed memory and corrupt - // its metadata. See - // https://github.com/google/sanitizers/issues/321. - SIZE_T res = REAL(wcrtomb)(dest, src, ps); - if (res != ((SIZE_T)-1) && dest) { - SIZE_T write_cnt = res; - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + + if (!dest) + return REAL(wcrtomb)(dest, src, ps); + + char local_dest[32]; + SIZE_T res = REAL(wcrtomb)(local_dest, src, ps); + if (res != ((SIZE_T)-1)) { + CHECK_LE(res, sizeof(local_dest)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, res); + REAL(memcpy)(dest, local_dest, res); } return res; } Index: compiler-rt/trunk/test/asan/TestCases/wcrtomb.c =================================================================== --- compiler-rt/trunk/test/asan/TestCases/wcrtomb.c +++ compiler-rt/trunk/test/asan/TestCases/wcrtomb.c @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + char *buff = (char*) malloc(MB_CUR_MAX); + free(buff); + wcrtomb(buff, L'a', NULL); + // CHECK: use-after-free + // CHECK: SUMMARY + return 0; +} Index: compiler-rt/trunk/test/sanitizer_common/TestCases/wcrtomb.c =================================================================== --- compiler-rt/trunk/test/sanitizer_common/TestCases/wcrtomb.c +++ compiler-rt/trunk/test/sanitizer_common/TestCases/wcrtomb.c @@ -0,0 +1,36 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include +#include +#include +#include + +int main(int argc, char **argv) { + mbstate_t state; + memset(&state, 0, sizeof(state)); + + char buff[10]; + size_t res = wcrtomb(buff, L'a', &state); + assert(res == 1); + assert(buff[0] == 'a'); + + res = wcrtomb(buff, L'\0', &state); + assert(res == 1); + assert(buff[0] == '\0'); + + res = wcrtomb(NULL, L'\0', &state); + assert(res == 1); + + res = wcrtomb(buff, L'a', NULL); + assert(res == 1); + assert(buff[0] == 'a'); + + res = wcrtomb(buff, L'\0', NULL); + assert(res == 1); + assert(buff[0] == '\0'); + + res = wcrtomb(NULL, L'\0', NULL); + assert(res == 1); + + return 0; +}