Index: asan/lit_tests/TestCases/printf-1.c =================================================================== --- asan/lit_tests/TestCases/printf-1.c +++ asan/lit_tests/TestCases/printf-1.c @@ -0,0 +1,13 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + printf("%c %d %s\n", c, x, s); + return 0; + // Check that printf works fine under Asan. + // CHECK: 0 12 34 +} Index: asan/lit_tests/TestCases/printf-2.c =================================================================== --- asan/lit_tests/TestCases/printf-2.c +++ asan/lit_tests/TestCases/printf-2.c @@ -0,0 +1,18 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + char *p = strdup((const char *)s); + free(p); + printf("%c %d %s\n", c, x, p); + return 0; + // Check that %s is checked + // CHECK: heap-use-after-free + // CHECK-NOT: 0 12 34 +} Index: asan/lit_tests/TestCases/printf-3.c =================================================================== --- asan/lit_tests/TestCases/printf-3.c +++ asan/lit_tests/TestCases/printf-3.c @@ -0,0 +1,17 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + volatile int n[1]; + printf("%c %d %s%n\n", c, x, s, &n[1]); + return 0; + // Check that %n is checked + // CHECK: stack-buffer-overflow + // CHECK-NOT: 0 12 34 +} Index: asan/lit_tests/TestCases/printf-4.c =================================================================== --- asan/lit_tests/TestCases/printf-4.c +++ asan/lit_tests/TestCases/printf-4.c @@ -0,0 +1,17 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + volatile char buf[2]; + sprintf((char *)buf, "%c %d %s\n", c, x, s); + return 0; + // Check that size of output buffer is checked + // CHECK: stack-buffer-overflow + // CHECK-NOT: 0 12 34 +} Index: asan/lit_tests/TestCases/printf-5.c =================================================================== --- asan/lit_tests/TestCases/printf-5.c +++ asan/lit_tests/TestCases/printf-5.c @@ -0,0 +1,18 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + volatile char fmt[2]; + memcpy((char *)fmt, "%c %d %s\n", sizeof(fmt)); + printf(fmt, c, x, s); + return 0; + // Check that format string is checked + // CHECK: stack-buffer-overflow + // CHECK-NOT: 0 12 34 +} Index: msan/msan_interceptors.cc =================================================================== --- msan/msan_interceptors.cc +++ msan/msan_interceptors.cc @@ -1291,6 +1291,8 @@ } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +// FIXME: update Msan to use common printf interceptors +#define SANITIZER_INTERCEPT_PRINTF 0 #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) Index: sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- sanitizer_common/sanitizer_common_interceptors.inc +++ sanitizer_common/sanitizer_common_interceptors.inc @@ -569,9 +569,23 @@ #define INIT_STRPTIME #endif -#if SANITIZER_INTERCEPT_SCANF +#if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF +# include "sanitizer_common_interceptors_scanf.inc" -#include "sanitizer_common_interceptors_scanf.inc" +#define FORMAT_INTERCEPTOR_IMPL(name, vname, ...) \ + { \ + void *ctx; \ + va_list ap; \ + va_start(ap, format); \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ + int res = vname(__VA_ARGS__, ap); \ + va_end(ap); \ + return res; \ + } + +#endif + +#if SANITIZER_INTERCEPT_SCANF #define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ { \ @@ -607,35 +621,24 @@ VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_SCANF -#define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ - { \ - void *ctx; \ - va_list ap; \ - va_start(ap, format); \ - COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ - int res = vname(__VA_ARGS__, ap); \ - va_end(ap); \ - return res; \ - } - INTERCEPTOR(int, scanf, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format) +FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format) INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) +FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) +FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) +FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) +FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) -SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) +FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif #endif @@ -664,6 +667,169 @@ #define INIT_ISOC99_SCANF #endif +#if SANITIZER_INTERCEPT_PRINTF + +#define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ + va_list aq; \ + va_copy(aq, ap); + +#define VPRINTF_INTERCEPTOR_RETURN() \ + va_end(aq); + +#define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \ + if (common_flags()->check_printf) \ + printf_common(ctx, format, aq); \ + int res = REAL(vname)(__VA_ARGS__); \ + VPRINTF_INTERCEPTOR_RETURN(); \ + return res; \ + } + +#define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ + if (common_flags()->check_printf) { \ + printf_common(ctx, format, aq); \ + } \ + int res = REAL(vname)(str, __VA_ARGS__); \ + if (res > 0 && common_flags()->check_printf) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \ + } \ + VPRINTF_INTERCEPTOR_RETURN(); \ + return res; \ + } + +#define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ + if (common_flags()->check_printf) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, size); \ + printf_common(ctx, format, aq); \ + } \ + int res = REAL(vname)(str, size, __VA_ARGS__); \ + if (res > 0 && common_flags()->check_printf) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \ + } \ + VPRINTF_INTERCEPTOR_RETURN(); \ + return res; \ + } + +#define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \ + if (common_flags()->check_printf) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *)); \ + printf_common(ctx, format, aq); \ + } \ + int res = REAL(vname)(strp, __VA_ARGS__); \ + if (res > 0 && common_flags()->check_printf) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res); \ + } \ + VPRINTF_INTERCEPTOR_RETURN(); \ + return res; \ + } + +INTERCEPTOR(int, vprintf, const char *format, va_list ap) +VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap) + +INTERCEPTOR(int, vfprintf, void *stream, const char *format, va_list ap) +VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap) + +INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) +VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap) + +INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format, + va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) + +INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) +VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap) + +#if SANITIZER_INTERCEPT_ISOC99_PRINTF +INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap) +VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap) + +INTERCEPTOR(int, __isoc99_vfprintf, void *stream, const char *format, + va_list ap) +VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap) + +INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format, + va_list ap) +VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format, ap) + +INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format, + va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap) + +#endif // SANITIZER_INTERCEPT_ISOC99_PRINTF + +INTERCEPTOR(int, printf, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format) + +INTERCEPTOR(int, fprintf, void *stream, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format) + +INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT +FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT + +INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format) + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format) + +#if SANITIZER_INTERCEPT_ISOC99_PRINTF +INTERCEPTOR(int, __isoc99_printf, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format) + +INTERCEPTOR(int, __isoc99_fprintf, void *stream, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format) + +INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format) + +INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size, + const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, + format) + +#endif // SANITIZER_INTERCEPT_ISOC99_PRINTF + +#endif // SANITIZER_INTERCEPT_PRINTF + +#if SANITIZER_INTERCEPT_PRINTF +#define INIT_PRINTF \ + COMMON_INTERCEPT_FUNCTION(printf); \ + COMMON_INTERCEPT_FUNCTION(sprintf); \ + COMMON_INTERCEPT_FUNCTION(snprintf); \ + COMMON_INTERCEPT_FUNCTION(asprintf); \ + COMMON_INTERCEPT_FUNCTION(fprintf); \ + COMMON_INTERCEPT_FUNCTION(vprintf); \ + COMMON_INTERCEPT_FUNCTION(vsprintf); \ + COMMON_INTERCEPT_FUNCTION(vsnprintf); \ + COMMON_INTERCEPT_FUNCTION(vasprintf); \ + COMMON_INTERCEPT_FUNCTION(vfprintf); +#else +#define INIT_PRINTF +#endif + +#if SANITIZER_INTERCEPT_ISOC99_PRINTF +#define INIT_ISOC99_PRINTF \ + COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf); +#else +#define INIT_ISOC99_PRINTF +#endif + #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { @@ -2924,6 +3090,8 @@ INIT_STRPTIME; \ INIT_SCANF; \ INIT_ISOC99_SCANF; \ + INIT_PRINTF; \ + INIT_ISOC99_PRINTF; \ INIT_FREXP; \ INIT_FREXPF_FREXPL; \ INIT_GETPWNAM_AND_FRIENDS; \ Index: sanitizer_common/sanitizer_common_interceptors_scanf.inc =================================================================== --- sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// Scanf implementation for use in *Sanitizer interceptors. +// Scanf/printf implementation for use in *Sanitizer interceptors. // Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html // with a few common GNU extensions. // @@ -35,11 +35,11 @@ return !!internal_strchr(s, c); } -// Parse scanf format string. If a valid directive in encountered, it is +// Parse format string. If a valid directive in encountered, it is // returned in dir. This function returns the pointer to the first // unprocessed character, or 0 in case of error. // In case of the end-of-string, a pointer to the closing \0 is returned. -static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, +static const char *format_parse_next(const char *p, bool allowGnuMalloc, ScanfDirective *dir) { internal_memset(dir, 0, sizeof(*dir)); dir->argIdx = -1; @@ -151,18 +151,18 @@ } // Returns true if the character is an integer conversion specifier. -static bool scanf_is_integer_conv(char c) { +static bool format_is_integer_conv(char c) { return char_is_one_of(c, "diouxXn"); } // Returns true if the character is an floating point conversion specifier. -static bool scanf_is_float_conv(char c) { +static bool format_is_float_conv(char c) { return char_is_one_of(c, "aAeEfFgG"); } // Returns string output character size for string-like conversions, // or 0 if the conversion is invalid. -static int scanf_get_char_size(ScanfDirective *dir) { +static int format_get_char_size(ScanfDirective *dir) { if (char_is_one_of(dir->convSpecifier, "CS")) { // wchar_t return 0; @@ -181,33 +181,33 @@ return 0; } -enum ScanfStoreSize { +enum FormatStoreSize { // Store size not known in advance; can be calculated as strlen() of the // destination buffer. - SSS_STRLEN = -1, + FSS_STRLEN = -1, // Invalid conversion specifier. - SSS_INVALID = 0 + FSS_INVALID = 0 }; -// Returns the store size of a scanf directive (if >0), or a value of +// Returns the store size of a format directive (if >0), or a value of // ScanfStoreSize. -static int scanf_get_store_size(ScanfDirective *dir) { +static int format_get_value_size(ScanfDirective *dir) { if (dir->allocate) { if (!char_is_one_of(dir->convSpecifier, "cCsS[")) - return SSS_INVALID; + return FSS_INVALID; return sizeof(char *); } if (dir->maybeGnuMalloc) { if (dir->convSpecifier != 'a' || dir->lengthModifier[0]) - return SSS_INVALID; + return FSS_INVALID; // This is ambiguous, so check the smaller size of char * (if it is // a GNU extension of %as, %aS or %a[...]) and float (if it is // POSIX %a followed by s, S or [ letters). return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float); } - if (scanf_is_integer_conv(dir->convSpecifier)) { + if (format_is_integer_conv(dir->convSpecifier)) { switch (dir->lengthModifier[0]) { case 'h': return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); @@ -224,11 +224,11 @@ case 0: return sizeof(int); default: - return SSS_INVALID; + return FSS_INVALID; } } - if (scanf_is_float_conv(dir->convSpecifier)) { + if (format_is_float_conv(dir->convSpecifier)) { switch (dir->lengthModifier[0]) { case 'L': case 'q': @@ -239,23 +239,23 @@ case 0: return sizeof(float); default: - return SSS_INVALID; + return FSS_INVALID; } } if (char_is_one_of(dir->convSpecifier, "sS[")) { - unsigned charSize = scanf_get_char_size(dir); + unsigned charSize = format_get_char_size(dir); if (charSize == 0) - return SSS_INVALID; + return FSS_INVALID; if (dir->fieldWidth == 0) - return SSS_STRLEN; + return FSS_STRLEN; return (dir->fieldWidth + 1) * charSize; } if (char_is_one_of(dir->convSpecifier, "cC")) { - unsigned charSize = scanf_get_char_size(dir); + unsigned charSize = format_get_char_size(dir); if (charSize == 0) - return SSS_INVALID; + return FSS_INVALID; if (dir->fieldWidth == 0) return charSize; return dir->fieldWidth * charSize; @@ -263,11 +263,11 @@ if (dir->convSpecifier == 'p') { if (dir->lengthModifier[1] != 0) - return SSS_INVALID; + return FSS_INVALID; return sizeof(void *); } - return SSS_INVALID; + return FSS_INVALID; } // Common part of *scanf interceptors. @@ -278,9 +278,11 @@ CHECK_GT(n_inputs, 0); const char *p = format; + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1); + while (*p) { ScanfDirective dir; - p = scanf_parse_next(p, allowGnuMalloc, &dir); + p = format_parse_next(p, allowGnuMalloc, &dir); if (!p) break; if (dir.convSpecifier == 0) { @@ -295,17 +297,77 @@ } if (dir.suppressed) continue; - int size = scanf_get_store_size(&dir); - if (size == SSS_INVALID) + int size = format_get_value_size(&dir); + if (size == FSS_INVALID) { + Report("WARNING: unexpected format specifier in scanf interceptor\n"); break; + } void *argp = va_arg(aq, void *); if (dir.convSpecifier != 'n') --n_inputs; if (n_inputs < 0) break; - if (size == SSS_STRLEN) { + if (size == FSS_STRLEN) { size = internal_strlen((const char *)argp) + 1; } COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + // FIXME: detect overlaps with format string? + } +} + +// Common part of *printf interceptors. +// Process format string and va_list, and report all load ranges. +static void printf_common(void *ctx, const char *format, va_list aq) { + const char *p = format; + + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1); + + while (*p) { + ScanfDirective dir; + p = format_parse_next(p, false, &dir); + if (!p) + break; + if (dir.convSpecifier == 0) { + // This can only happen at the end of the format string. + CHECK_EQ(*p, 0); + break; + } + // Here the directive is valid. Do what it says. + if (dir.argIdx != -1) { + // Unsupported. + break; + } + if (dir.suppressed) + continue; + int size = format_get_value_size(&dir); + if (size == FSS_INVALID) { + Report("WARNING: unexpected format specifier in printf interceptor\n"); + break; + } + if (dir.convSpecifier == 'n') { + void *argp = va_arg(aq, void *); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + // FIXME: detect overlaps with format string? + continue; + } else if (size == FSS_STRLEN) { + void *argp = va_arg(aq, void *); + size = internal_strlen((const char *)argp) + 1; + COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size); + } else { + // Skip non-pointer args + switch (size) { + case 1: + case 2: + case 4: + va_arg(aq, u32); + break; + case 8: + va_arg(aq, u64); + break; + default: + CHECK(0); + break; + } + } } } Index: sanitizer_common/sanitizer_flags.h =================================================================== --- sanitizer_common/sanitizer_flags.h +++ sanitizer_common/sanitizer_flags.h @@ -56,6 +56,8 @@ bool allocator_may_return_null; // If false, disable printing error summaries in addition to error reports. bool print_summary; + // Check printf arguments. + bool check_printf; }; inline CommonFlags *common_flags() { Index: sanitizer_common/sanitizer_flags.cc =================================================================== --- sanitizer_common/sanitizer_flags.cc +++ sanitizer_common/sanitizer_flags.cc @@ -32,6 +32,7 @@ f->leak_check_at_exit = true; f->allocator_may_return_null = false; f->print_summary = true; + f->check_printf = true; } void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { @@ -48,6 +49,7 @@ ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit"); ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null"); ParseFlag(str, &f->print_summary, "print_summary"); + ParseFlag(str, &f->check_printf, "check_printf"); // Do a sanity check for certain flags. if (f->malloc_context_size < 1) Index: sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- sanitizer_common/sanitizer_platform_interceptors.h +++ sanitizer_common/sanitizer_platform_interceptors.h @@ -75,6 +75,11 @@ #define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX +#ifndef SANITIZER_INTERCEPT_PRINTF +# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX +#endif + #define SANITIZER_INTERCEPT_FREXP 1 #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS Index: tsan/lit_tests/printf-1.c =================================================================== --- tsan/lit_tests/printf-1.c +++ tsan/lit_tests/printf-1.c @@ -0,0 +1,13 @@ +// RUN: %clang_tsan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile char s[] = "34"; + printf("%c %d %s\n", c, x, s); + return 0; + // Check that printf works fine under Tsan. + // CHECK: 0 12 34 +} Index: tsan/rtl/tsan_stat.h =================================================================== --- tsan/rtl/tsan_stat.h +++ tsan/rtl/tsan_stat.h @@ -278,6 +278,26 @@ StatInt___isoc99_scanf, StatInt___isoc99_sscanf, StatInt___isoc99_fscanf, + StatInt_vprintf, + StatInt_vsprintf, + StatInt_vsnprintf, + StatInt_vasprintf, + StatInt_vfprintf, + StatInt_printf, + StatInt_sprintf, + StatInt_snprintf, + StatInt_asprintf, + StatInt_fprintf, + StatInt___isoc99_vprintf, + StatInt___isoc99_vsprintf, + StatInt___isoc99_vsnprintf, + StatInt___isoc99_vasprintf, + StatInt___isoc99_vfprintf, + StatInt___isoc99_printf, + StatInt___isoc99_sprintf, + StatInt___isoc99_snprintf, + StatInt___isoc99_asprintf, + StatInt___isoc99_fprintf, StatInt_on_exit, StatInt___cxa_atexit, StatInt_localtime, Index: tsan/rtl/tsan_stat.cc =================================================================== --- tsan/rtl/tsan_stat.cc +++ tsan/rtl/tsan_stat.cc @@ -283,6 +283,24 @@ name[StatInt___isoc99_scanf] = " scanf "; name[StatInt___isoc99_sscanf] = " sscanf "; name[StatInt___isoc99_fscanf] = " fscanf "; + name[StatInt_vprintf] = " vprintf "; + name[StatInt_vsprintf] = " vsprintf "; + name[StatInt_vsnprintf] = " vsnprintf "; + name[StatInt_vasprintf] = " vasprintf "; + name[StatInt_vfprintf] = " vfprintf "; + name[StatInt_printf] = " printf "; + name[StatInt_sprintf] = " sprintf "; + name[StatInt_snprintf] = " snprintf "; + name[StatInt_asprintf] = " asprintf "; + name[StatInt_fprintf] = " fprintf "; + name[StatInt___isoc99_vprintf] = " vprintf "; + name[StatInt___isoc99_vsprintf] = " vsprintf "; + name[StatInt___isoc99_vsnprintf] = " vsnprintf "; + name[StatInt___isoc99_vfprintf] = " vfprintf "; + name[StatInt___isoc99_printf] = " printf "; + name[StatInt___isoc99_sprintf] = " sprintf "; + name[StatInt___isoc99_snprintf] = " snprintf "; + name[StatInt___isoc99_fprintf] = " fprintf "; name[StatInt_on_exit] = " on_exit "; name[StatInt___cxa_atexit] = " __cxa_atexit "; name[StatInt_localtime] = " localtime ";