Index: lib/asan/lit_tests/TestCases/printf-1.c =================================================================== --- lib/asan/lit_tests/TestCases/printf-1.c +++ lib/asan/lit_tests/TestCases/printf-1.c @@ -0,0 +1,16 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s +// RUN: %t 2>&1 | FileCheck %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + printf("%c %d %.3f %s\n", c, x, f, s); + return 0; + // Check that printf works fine under Asan. + // CHECK: 0 12 1.239 34 +} Index: lib/asan/lit_tests/TestCases/printf-2.c =================================================================== --- lib/asan/lit_tests/TestCases/printf-2.c +++ lib/asan/lit_tests/TestCases/printf-2.c @@ -0,0 +1,22 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + char *p = strdup((const char *)s); + free(p); + printf("%c %d %.3f %s\n", c, x, f, p); + return 0; + // Check that %s is sanitized. + // CHECK-ON: heap-use-after-free + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 +} Index: lib/asan/lit_tests/TestCases/printf-3.c =================================================================== --- lib/asan/lit_tests/TestCases/printf-3.c +++ lib/asan/lit_tests/TestCases/printf-3.c @@ -0,0 +1,19 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile int n[1]; + printf("%c %d %.3f %s%n\n", c, x, f, s, &n[1]); + return 0; + // Check that %n is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 34 +} Index: lib/asan/lit_tests/TestCases/printf-4.c =================================================================== --- lib/asan/lit_tests/TestCases/printf-4.c +++ lib/asan/lit_tests/TestCases/printf-4.c @@ -0,0 +1,20 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char buf[2]; + sprintf((char *)buf, "%c %d %.3f %s\n", c, x, f, s); + puts((const char *)buf); + return 0; + // Check that size of output buffer is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 34 +} Index: lib/asan/lit_tests/TestCases/printf-5.c =================================================================== --- lib/asan/lit_tests/TestCases/printf-5.c +++ lib/asan/lit_tests/TestCases/printf-5.c @@ -0,0 +1,21 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char fmt[2]; + memcpy((char *)fmt, "%c %d %f %s\n", sizeof(fmt)); + printf(fmt, c, x, f, s); + return 0; + // Check that format string is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 +} Index: lib/msan/msan_interceptors.cc =================================================================== --- lib/msan/msan_interceptors.cc +++ lib/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: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -569,10 +569,24 @@ #define INIT_STRPTIME #endif -#if SANITIZER_INTERCEPT_SCANF - +#if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF #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, ...) \ { \ void *ctx; \ @@ -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,167 @@ #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__); \ + 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 + 1); \ + } \ + 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, vsnprintf, char *str, SIZE_T size, const char *format, + va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) + +INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) +VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, 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_vsnprintf, char *str, SIZE_T size, const char *format, + va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap) + +INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format, + va_list ap) +VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, 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 +3088,8 @@ INIT_STRPTIME; \ INIT_SCANF; \ INIT_ISOC99_SCANF; \ + INIT_PRINTF; \ + INIT_ISOC99_PRINTF; \ INIT_FREXP; \ INIT_FREXPF_FREXPL; \ INIT_GETPWNAM_AND_FRIENDS; \ Index: lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -7,23 +7,14 @@ // //===----------------------------------------------------------------------===// // -// 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 +// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html // with a few common GNU extensions. // //===----------------------------------------------------------------------===// #include -struct ScanfDirective { - int argIdx; // argument index, or -1 of not specified ("%n$") - int fieldWidth; - bool suppressed; // suppress assignment ("*") - bool allocate; // allocate space ("m") - char lengthModifier[2]; - char convSpecifier; - bool maybeGnuMalloc; -}; - static const char *parse_number(const char *p, int *out) { *out = internal_atoll(p); while (*p >= '0' && *p <= '9') @@ -31,10 +22,179 @@ return p; } +static const char *maybe_parse_number(const char *p, int *out) { + if (*p >= '0' && *p <= '9') + p = parse_number(p, out); + return p; +} + +static const char *maybe_parse_number_or_star(const char *p, int *out, + bool *star) { + if (*p == '*') { + *star = true; + ++p; + } else { + *star = false; + p = maybe_parse_number(p, out); + } + return p; +} + +static const char *maybe_parse_param_index(const char *p, int *out) { + // n$ + if (*p >= '0' && *p <= '9') { + int number; + const char *q = parse_number(p, &number); + CHECK(q); + if (*q == '$') { + *out = number; + p = q + 1; + } + } + + // Otherwise, do not change p. This will be re-parsed later as the field + // width. + return p; +} + static bool char_is_one_of(char c, const char *s) { return !!internal_strchr(s, c); } +static const char *maybe_parse_length_modifier(const char *p, char ll[2]) { + if (char_is_one_of(*p, "jztLq")) { + ll[0] = *p; + ++p; + } else if (*p == 'h') { + ll[0] = 'h'; + ++p; + if (*p == 'h') { + ll[1] = 'h'; + ++p; + } + } else if (*p == 'l') { + ll[0] = 'l'; + ++p; + if (*p == 'l') { + ll[1] = 'l'; + ++p; + } + } + return p; +} + +// Returns true if the character is an integer conversion specifier. +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 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 format_get_char_size(char convSpecifier, + const char lengthModifier[2]) { + if (char_is_one_of(convSpecifier, "CS")) { + // wchar_t + return 0; + } + + if (char_is_one_of(convSpecifier, "cs[")) { + if (lengthModifier[0] == 'l') + // wchar_t + return 0; + else if (lengthModifier[0] == 0) + return sizeof(char); + else + return 0; + } + + return 0; +} + +enum FormatStoreSize { + // Store size not known in advance; can be calculated as strlen() of the + // destination buffer. + FSS_STRLEN = -1, + // Invalid conversion specifier. + FSS_INVALID = 0 +}; + +// Returns the memory size of a format directive (if >0), or a value of +// FormatStoreSize. +static int format_get_value_size(char convSpecifier, + const char lengthModifier[2], int fieldWidth, + bool promote_float) { + if (format_is_integer_conv(convSpecifier)) { + switch (lengthModifier[0]) { + case 'h': + return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); + case 'l': + return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); + case 'L': + return sizeof(long long); + case 'j': + return sizeof(INTMAX_T); + case 'z': + return sizeof(SIZE_T); + case 't': + return sizeof(PTRDIFF_T); + case 0: + return sizeof(int); + default: + return FSS_INVALID; + } + } + + if (format_is_float_conv(convSpecifier)) { + switch (lengthModifier[0]) { + case 'L': + case 'q': + return sizeof(long double); + case 'l': + return lengthModifier[1] == 'l' ? sizeof(long double) + : sizeof(double); + case 0: + // Printf promotes floats to doubles but scanf does not + return promote_float ? sizeof(double) : sizeof(float); + default: + return FSS_INVALID; + } + } + + if (char_is_one_of(convSpecifier, "cC")) { + unsigned charSize = format_get_char_size(convSpecifier, lengthModifier); + if (charSize == 0) + return FSS_INVALID; + if (fieldWidth == 0) + return charSize; + return fieldWidth * charSize; + } + + if (convSpecifier == 'p') { + if (lengthModifier[0] != 0) + return FSS_INVALID; + return sizeof(void *); + } + + return FSS_INVALID; +} + +struct ScanfDirective { + int argIdx; // argument index, or -1 if not specified ("%n$") + int fieldWidth; + const char *begin; + const char *end; + bool suppressed; // suppress assignment ("*") + bool allocate; // allocate space ("m") + char lengthModifier[2]; + char convSpecifier; + bool maybeGnuMalloc; +}; + // Parse scanf 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. @@ -49,6 +209,7 @@ ++p; continue; } + dir->begin = p; ++p; // %% if (*p == '%') { @@ -59,25 +220,18 @@ return 0; } // %n$ - if (*p >= '0' && *p <= '9') { - int number; - const char *q = parse_number(p, &number); - if (*q == '$') { - dir->argIdx = number; - p = q + 1; - } - // Otherwise, do not change p. This will be re-parsed later as the field - // width. - } + p = maybe_parse_param_index(p, &dir->argIdx); + CHECK(p); // * if (*p == '*') { dir->suppressed = true; ++p; } - // Field width. + // Field width if (*p >= '0' && *p <= '9') { p = parse_number(p, &dir->fieldWidth); - if (dir->fieldWidth <= 0) + CHECK(p); + if (dir->fieldWidth <= 0) // Width if at all must be non-zero return 0; } // m @@ -86,24 +240,7 @@ ++p; } // Length modifier. - if (char_is_one_of(*p, "jztLq")) { - dir->lengthModifier[0] = *p; - ++p; - } else if (*p == 'h') { - dir->lengthModifier[0] = 'h'; - ++p; - if (*p == 'h') { - dir->lengthModifier[1] = 'h'; - ++p; - } - } else if (*p == 'l') { - dir->lengthModifier[0] = 'l'; - ++p; - if (*p == 'l') { - dir->lengthModifier[1] = 'l'; - ++p; - } - } + p = maybe_parse_length_modifier(p, dir->lengthModifier); // Conversion specifier. dir->convSpecifier = *p++; // Consume %[...] expression. @@ -145,129 +282,40 @@ dir->maybeGnuMalloc = true; } } + dir->end = p; break; } return p; } -// Returns true if the character is an integer conversion specifier. -static bool scanf_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) { - 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) { - if (char_is_one_of(dir->convSpecifier, "CS")) { - // wchar_t - return 0; - } - - if (char_is_one_of(dir->convSpecifier, "cs[")) { - if (dir->lengthModifier[0] == 'l') - // wchar_t - return 0; - else if (dir->lengthModifier[0] == 0) - return sizeof(char); - else - return 0; - } - - return 0; -} - -enum ScanfStoreSize { - // Store size not known in advance; can be calculated as strlen() of the - // destination buffer. - SSS_STRLEN = -1, - // Invalid conversion specifier. - SSS_INVALID = 0 -}; - -// Returns the store size of a scanf directive (if >0), or a value of -// ScanfStoreSize. -static int scanf_get_store_size(ScanfDirective *dir) { +static int scanf_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)) { - switch (dir->lengthModifier[0]) { - case 'h': - return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); - case 'l': - return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); - case 'L': - return sizeof(long long); - case 'j': - return sizeof(INTMAX_T); - case 'z': - return sizeof(SIZE_T); - case 't': - return sizeof(PTRDIFF_T); - case 0: - return sizeof(int); - default: - return SSS_INVALID; - } - } - - if (scanf_is_float_conv(dir->convSpecifier)) { - switch (dir->lengthModifier[0]) { - case 'L': - case 'q': - return sizeof(long double); - case 'l': - return dir->lengthModifier[1] == 'l' ? sizeof(long double) - : sizeof(double); - case 0: - return sizeof(float); - default: - return SSS_INVALID; - } - } - if (char_is_one_of(dir->convSpecifier, "sS[")) { - unsigned charSize = scanf_get_char_size(dir); + unsigned charSize = format_get_char_size(dir->convSpecifier, + dir->lengthModifier); 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); - if (charSize == 0) - return SSS_INVALID; - if (dir->fieldWidth == 0) - return charSize; - return dir->fieldWidth * charSize; - } - - if (dir->convSpecifier == 'p') { - if (dir->lengthModifier[1] != 0) - return SSS_INVALID; - return sizeof(void *); - } - - return SSS_INVALID; + return format_get_value_size(dir->convSpecifier, dir->lengthModifier, + dir->fieldWidth, false); } // Common part of *scanf interceptors. @@ -278,6 +326,8 @@ 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); @@ -295,17 +345,191 @@ } if (dir.suppressed) continue; - int size = scanf_get_store_size(&dir); - if (size == SSS_INVALID) + int size = scanf_get_value_size(&dir); + if (size == FSS_INVALID) { + Report("WARNING: unexpected format specifier in scanf interceptor: " + "%.*s\n", dir.end - dir.begin, dir.begin); 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); } } + +struct PrintfDirective { + int fieldWidth; + int fieldPrecision; + int argIdx; // width argument index, or -1 if not specified ("%*n$") + int precisionIdx; // precision argument index, or -1 if not specified (".*n$") + const char *begin; + const char *end; + bool starredWidth; + bool starredPrecision; + char lengthModifier[2]; + char convSpecifier; +}; + +// Parse printf format string. Same as scanf_parse_next. +static const char *printf_parse_next(const char *p, PrintfDirective *dir) { + internal_memset(dir, 0, sizeof(*dir)); + dir->argIdx = dir->precisionIdx = -1; + + while (*p) { + if (*p != '%') { + ++p; + continue; + } + dir->begin = p; + ++p; + // %% + if (*p == '%') { + ++p; + continue; + } + if (*p == '\0') { + return 0; + } + // %n$ + p = maybe_parse_param_index(p, &dir->precisionIdx); + CHECK(p); + // Flags + while (char_is_one_of(*p, "'-+ #0")) { + ++p; + } + // Field width + p = maybe_parse_number_or_star(p, &dir->fieldWidth, + &dir->starredWidth); + if (!p) + return 0; + // Precision + if (*p == '.') { + ++p; + // Actual precision is optional (surprise!) + p = maybe_parse_number_or_star(p, &dir->fieldPrecision, + &dir->starredPrecision); + if (!p) + return 0; + // m$ + if (dir->starredPrecision) { + p = maybe_parse_param_index(p, &dir->precisionIdx); + CHECK(p); + } + } + // Length modifier. + p = maybe_parse_length_modifier(p, dir->lengthModifier); + // Conversion specifier. + dir->convSpecifier = *p++; + dir->end = p; + break; + } + return p; +} + +static int printf_get_value_size(PrintfDirective *dir) { + if (dir->convSpecifier == 'm') { + return sizeof(char *); + } + + if (char_is_one_of(dir->convSpecifier, "sS")) { + unsigned charSize = format_get_char_size(dir->convSpecifier, + dir->lengthModifier); + if (charSize == 0) + return FSS_INVALID; + return FSS_STRLEN; + } + + return format_get_value_size(dir->convSpecifier, dir->lengthModifier, + dir->fieldWidth, true); +} + +#define SKIP_SCALAR_ARG(aq, convSpecifier, size) \ + do { \ + if (format_is_float_conv(convSpecifier)) { \ + switch (size) { \ + case 8: \ + va_arg(*aq, double); \ + break; \ + case 16: \ + va_arg(*aq, long double); \ + break; \ + default: \ + Report("WARNING: unexpected floating-point arg size" \ + " in printf interceptor: %d\n", size); \ + return; \ + } \ + } else { \ + switch (size) { \ + case 1: \ + case 2: \ + case 4: \ + va_arg(*aq, u32); \ + break; \ + case 8: \ + va_arg(*aq, u64); \ + break; \ + default: \ + Report("WARNING: unexpected arg size" \ + " in printf interceptor: %d\n", size); \ + return; \ + } \ + } \ + } while (0) + +// 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) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1); + + const char *p = format; + + while (*p) { + PrintfDirective dir; + p = printf_parse_next(p, &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 || dir.precisionIdx != -1) { + // Unsupported. + break; + } + if (dir.starredWidth) { + // Dynamic width + SKIP_SCALAR_ARG(&aq, 'd', sizeof(int)); + } + if (dir.starredPrecision) { + // Dynamic precision + SKIP_SCALAR_ARG(&aq, 'd', sizeof(int)); + } + int size = printf_get_value_size(&dir); + if (size == FSS_INVALID) { + Report("WARNING: unexpected format specifier in printf " + "interceptor: %.*s\n", dir.end - dir.begin, dir.begin); + break; + } + if (dir.convSpecifier == 'n') { + void *argp = va_arg(aq, void *); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + continue; + } else if (size == FSS_STRLEN) { + if (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 + SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size); + } + } +} Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/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: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/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 = false; } 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: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/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: lib/sanitizer_common/sanitizer_printf.cc =================================================================== --- lib/sanitizer_common/sanitizer_printf.cc +++ lib/sanitizer_common/sanitizer_printf.cc @@ -95,11 +95,14 @@ minimal_num_length, pad_with_zero, negative); } -static int AppendString(char **buff, const char *buff_end, const char *s) { +static int AppendString(char **buff, const char *buff_end, int precision, + const char *s) { if (s == 0) s = ""; int result = 0; for (; *s; s++) { + if (precision >= 0 && result >= precision) + break; result += AppendChar(buff, buff_end, *s); } return result; @@ -107,7 +110,7 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { int result = 0; - result += AppendString(buff, buff_end, "0x"); + result += AppendString(buff, buff_end, -1, "0x"); result += AppendUnsigned(buff, buff_end, ptr_value, 16, (SANITIZER_WORDSIZE == 64) ? 12 : 8, true); return result; @@ -116,7 +119,7 @@ int VSNPrintf(char *buff, int buff_length, const char *format, va_list args) { static const char *kPrintfFormatsHelp = - "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n"; + "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n"; RAW_CHECK(format); RAW_CHECK(buff_length > 0); const char *buff_end = &buff[buff_length - 1]; @@ -136,6 +139,12 @@ width = width * 10 + *cur++ - '0'; } } + bool have_precision = (cur[0] == '.' && cur[1] == '*'); + int precision = -1; + if (have_precision) { + cur += 2; + precision = va_arg(args, int); + } bool have_z = (*cur == 'z'); cur += have_z; bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l'); @@ -143,6 +152,8 @@ s64 dval; u64 uval; bool have_flags = have_width | have_z | have_ll; + // Only %s supports precision for now + CHECK(!(precision >= 0 && *cur != 's')); switch (*cur) { case 'd': { dval = have_ll ? va_arg(args, s64) @@ -168,7 +179,7 @@ } case 's': { RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); - result += AppendString(&buff, buff_end, va_arg(args, char*)); + result += AppendString(&buff, buff_end, precision, va_arg(args, char*)); break; } case 'c': { Index: lib/sanitizer_common/tests/sanitizer_printf_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_printf_test.cc +++ lib/sanitizer_common/tests/sanitizer_printf_test.cc @@ -135,4 +135,11 @@ TestAgainstLibc("%03d - %03d", -12, -1234); } +TEST(Printf, Precision) { + char buf[1024]; + uptr len = internal_snprintf(buf, sizeof(buf), "%.*s", 3, "12345"); + EXPECT_EQ(len, strlen(buf)); + EXPECT_STREQ("123", buf); +} + } // namespace __sanitizer Index: lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc +++ lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc @@ -15,17 +15,51 @@ #include "interception/interception.h" #include "sanitizer_test_utils.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_common.h" #include "gtest/gtest.h" using namespace __sanitizer; +#define COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) \ + do { \ + ((std::vector *)ctx)->push_back(size); \ + ptr = ptr; \ + } while (0) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) + #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - ((std::vector *)ctx)->push_back(size) + COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size) #include "sanitizer_common/sanitizer_common_interceptors_scanf.inc" -static const char scanf_buf[] = "Test string."; -static size_t scanf_buf_size = sizeof(scanf_buf); +static const unsigned I = sizeof(int); +static const unsigned L = sizeof(long); +static const unsigned LL = sizeof(long long); +static const unsigned S = sizeof(short); +static const unsigned C = sizeof(char); +static const unsigned D = sizeof(double); +static const unsigned LD = sizeof(long double); +static const unsigned F = sizeof(float); +static const unsigned P = sizeof(char *); + +static void verifyFormatResults(const char *format, unsigned n, + const std::vector &computed_sizes, + va_list expected_sizes) { + // "+ 1" because of 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]) + << "Unexpect write size for argument " << i << ", format string '" + << format << "'"; +} + +static const char test_buf[] = "Test string."; +static const size_t test_buf_size = sizeof(test_buf); + static const unsigned SCANF_ARGS_MAX = 16; static void testScanf3(void *ctx, int result, bool allowGnuMalloc, @@ -42,15 +76,10 @@ std::vector scanf_sizes; // 16 args should be enough. testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format, - scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, - scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, - scanf_buf, scanf_buf, scanf_buf, scanf_buf); - ASSERT_EQ(n, scanf_sizes.size()) << "Unexpected number of format arguments: '" - << format << "'"; - for (unsigned i = 0; i < n; ++i) - EXPECT_EQ(va_arg(expected_sizes, unsigned), scanf_sizes[i]) - << "Unexpect write size for argument " << i << ", format string '" - << format << "'"; + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf); + verifyFormatResults(format, n, scanf_sizes, expected_sizes); } static void testScanf(const char *format, unsigned n, ...) { @@ -76,16 +105,6 @@ } TEST(SanitizerCommonInterceptors, Scanf) { - const unsigned I = sizeof(int); // NOLINT - const unsigned L = sizeof(long); // NOLINT - const unsigned LL = sizeof(long long); // NOLINT - const unsigned S = sizeof(short); // NOLINT - const unsigned C = sizeof(char); // NOLINT - const unsigned D = sizeof(double); // NOLINT - const unsigned LD = sizeof(long double); // NOLINT - const unsigned F = sizeof(float); // NOLINT - const unsigned P = sizeof(char *); // NOLINT - testScanf("%d", 1, I); testScanf("%d%d%d", 3, I, I, I); testScanf("ab%u%dc", 2, I, I); @@ -113,10 +132,10 @@ testScanf("%*d", 0); testScanf("%4d%8f%c", 3, I, F, C); - testScanf("%s%d", 2, scanf_buf_size, I); - testScanf("%[abc]", 1, scanf_buf_size); + testScanf("%s%d", 2, test_buf_size, I); + testScanf("%[abc]", 1, test_buf_size); testScanf("%4[bcdef]", 1, 5); - testScanf("%[]]", 1, scanf_buf_size); + testScanf("%[]]", 1, test_buf_size); testScanf("%8[^]%d0-9-]%c", 2, 9, C); testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I); @@ -172,7 +191,54 @@ testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, I); testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I); - testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, scanf_buf_size); - testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, scanf_buf_size, - scanf_buf_size); + testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, test_buf_size); + testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, test_buf_size, + test_buf_size); +} + +static void testPrintf3(void *ctx, const char *format, ...) { + va_list ap; + va_start(ap, format); + printf_common(ctx, format, ap); + va_end(ap); +} + +static void testPrintf2(const char *format, unsigned n, + va_list expected_sizes) { + std::vector printf_sizes; + // 16 args should be enough. + testPrintf3((void *)&printf_sizes, format, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf, test_buf, test_buf, + test_buf, test_buf, test_buf, test_buf); + verifyFormatResults(format, n, printf_sizes, expected_sizes); +} + +static void testPrintf(const char *format, unsigned n, ...) { + va_list ap; + va_start(ap, n); + testPrintf2(format, n, ap); + va_end(ap); +} + +TEST(SanitizerCommonInterceptors, Printf) { + // Only test functionality which differs from scanf + + // Indexed arguments + testPrintf("%5$d", 0); + testPrintf("%.*5$d", 0); + + // errno + testPrintf("%0-m", 0); + + // Dynamic width + testPrintf("%*n", 1, I); + testPrintf("%*.10n", 1, I); + + // Precision + testPrintf("%10.10n", 1, I); + + // Dynamic precision + testPrintf("%.*n", 1, I); + testPrintf("%10.*n", 1, I); } Index: lib/tsan/lit_tests/printf-1.c =================================================================== --- lib/tsan/lit_tests/printf-1.c +++ lib/tsan/lit_tests/printf-1.c @@ -0,0 +1,16 @@ +// RUN: %clang_tsan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s +// RUN: %t 2>&1 | FileCheck %s + +#include +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + printf("%c %d %.3f %s\n", c, x, f, s); + return 0; + // Check that printf works fine under Tsan. + // CHECK: 0 12 1.239 34 +} Index: lib/tsan/rtl/tsan_stat.h =================================================================== --- lib/tsan/rtl/tsan_stat.h +++ lib/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: lib/tsan/rtl/tsan_stat.cc =================================================================== --- lib/tsan/rtl/tsan_stat.cc +++ lib/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 ";