diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc @@ -340,6 +340,14 @@ size = 0; } COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + // For %ms/%mc, write the allocated output buffer as well. + if (dir.allocate) { + char *buf = *(char **)argp; + if (buf) { + uptr buf_len = internal_strlen(buf) + 1; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buf_len); + } + } } } diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cpp @@ -48,13 +48,13 @@ static void verifyFormatResults(const char *format, unsigned n, const std::vector &computed_sizes, - va_list expected_sizes) { - // "+ 1" because of format string + const std::vector &expected_sizes) { + // "+ 1" because of the format string ASSERT_EQ(n + 1, computed_sizes.size()) << "Unexpected number of format arguments: '" << format << "'"; for (unsigned i = 0; i < n; ++i) - EXPECT_EQ(va_arg(expected_sizes, unsigned), computed_sizes[i + 1]) + EXPECT_EQ(expected_sizes[i], computed_sizes[i + 1]) << "Unexpect write size for argument " << i << ", format string '" << format << "'"; } @@ -74,8 +74,11 @@ static void testScanf2(const char *format, int scanf_result, bool allowGnuMalloc, unsigned n, - va_list expected_sizes) { - std::vector scanf_sizes; + va_list expected_sizes_va) { + std::vector scanf_sizes, expected_sizes; + for (unsigned i = 0; i < n; ++i) + expected_sizes.push_back(va_arg(expected_sizes_va, unsigned)); + // 16 args should be enough. testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format, test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, @@ -151,7 +154,6 @@ testScanf("%c%d", 2, C, I); testScanf("%A%lf", 2, F, D); - testScanf("%ms %Lf", 2, P, LD); testScanf("s%Las", 1, LD); testScanf("%ar", 1, F); @@ -202,6 +204,26 @@ test_buf_size); } +TEST(SanitizerCommonInterceptors, ScanfAllocate) { + const char *buf = "123456"; + + // Can not use testScanf() because this case needs a valid pointer to a string + // in the scanf argument. + { + std::vector scanf_sizes; + testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%ms", &buf); + verifyFormatResults("%ms", 2, scanf_sizes, + {P, (unsigned)(strlen(buf) + 1)}); + } + + { + std::vector scanf_sizes; + testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%mc", &buf); + verifyFormatResults("%mc", 2, scanf_sizes, + {P, (unsigned)(strlen(buf) + 1)}); + } +} + static void testPrintf3(void *ctx, const char *format, ...) { va_list ap; va_start(ap, format); @@ -210,8 +232,11 @@ } static void testPrintf2(const char *format, unsigned n, - va_list expected_sizes) { - std::vector printf_sizes; + va_list expected_sizes_va) { + std::vector printf_sizes, expected_sizes; + for (unsigned i = 0; i < n; ++i) + expected_sizes.push_back(va_arg(expected_sizes_va, unsigned)); + // 16 args should be enough. testPrintf3((void *)&printf_sizes, format, test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, diff --git a/compiler-rt/test/msan/scanf-allocate.cpp b/compiler-rt/test/msan/scanf-allocate.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/msan/scanf-allocate.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -O0 %s -o %t && %run %t >%t.out 2>&1 +// FileCheck %s <%t.out + +#include +#include +#include + +int main(int argc, char **argv) { + char *str; + sscanf("#string#", "%ms", &str); + printf("str = %s\n", str); + __msan_check_mem_is_initialized(str, strlen(str) + 1); + // CHECK: #string# +}