Index: asan/asan_intercepted_functions.h =================================================================== --- asan/asan_intercepted_functions.h +++ asan/asan_intercepted_functions.h @@ -76,4 +76,16 @@ # define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif +#if SANITIZER_INTERCEPT_SCANF +# define ASAN_INTERCEPT_PRINTF 1 +#else +# define ASAN_INTERCEPT_PRINTF 0 +#endif + +#if SANITIZER_INTERCEPT_ISO99_SCANF +# define ASAN_INTERCEPT_ISO99_PRINTF 1 +#else +# define ASAN_INTERCEPT_ISO99_PRINTF 0 +#endif + #endif // ASAN_INTERCEPTED_FUNCTIONS_H Index: asan/asan_interceptors.cc =================================================================== --- asan/asan_interceptors.cc +++ asan/asan_interceptors.cc @@ -23,6 +23,8 @@ #include "asan_stats.h" #include "sanitizer_common/sanitizer_libc.h" +#include + namespace __asan { // Return true if we can quickly decide that the region is unpoisoned. @@ -672,6 +674,116 @@ } #endif // ASAN_INTERCEPT___CXA_ATEXIT +#if ASAN_INTERCEPT_PRINTF +// TODO(ygribov): move to sanitizer_common_interceptors.inc +// to reuse in e.g. other sanitizers? +#define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ + va_list aq; \ + va_copy(aq, ap); \ + printf_common(ctx, format, aq); \ + int res = REAL(vname)(__VA_ARGS__); \ + +#define VPRINTF_INTERCEPTOR_RETURN() \ + va_end(aq); \ + return res; + +#define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \ + VPRINTF_INTERCEPTOR_RETURN(); \ + } + +#define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ + { \ + VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ + COMMON_INTERCEPTOR_READ_RANGE(ctx, str, res); \ + VPRINTF_INTERCEPTOR_RETURN(); \ + } + +#define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ + { \ + COMMON_INTERCEPTOR_READ_RANGE(ctx, str, size); \ + VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ + VPRINTF_INTERCEPTOR_RETURN(); \ + } + +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) +VPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap) + +#if ASAN_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) + +// TODO(ygribov): __isoc99_vasprintf ? + +#endif // ASAN_INTERCEPT_ISOC99_PRINTF + +#define PRINTF_INTERCEPTOR_IMPL(name, vname, ...) \ + SCANF_INTERCEPTOR_IMPL(name, vname, __VA_ARGS__) + +INTERCEPTOR(int, printf, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(printf, vprintf, format) + +INTERCEPTOR(int, fprintf, void *stream, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format) + +INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT +PRINTF_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT + +INTERCEPTOR(int, snprintf, char *str, size_t size, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format) + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format) + +#if ASAN_INTERCEPT_ISOC99_PRINTF +INTERCEPTOR(int, __isoc99_printf, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format) + +INTERCEPTOR(int, __isoc99_fprintf, void *stream, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format) + +INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format) + +INTERCEPTOR(int, __isoc99_snprintf, char *str, size_t size, + const char *format, ...) +PRINTF_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, + format) + +// TODO(ygribov): __isoc99_asprintf ? + +#endif // ASAN_INTERCEPT_ISOC99_PRINTF + +#endif // ASAN_INTERCEPT_PRINTF + #if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, @@ -779,6 +891,32 @@ ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif + // Intercept printf family. +#if ASAN_INTERCEPT_PRINTF + ASAN_INTERCEPT_FUNC(vprintf); + ASAN_INTERCEPT_FUNC(vfprintf); + ASAN_INTERCEPT_FUNC(vsprintf); + ASAN_INTERCEPT_FUNC(vsnprintf); + ASAN_INTERCEPT_FUNC(vasprintf); +#if ASAN_INTERCEPT_FUNC_ISOC99_PRINTF + ASAN_INTERCEPT_FUNC(__isoc99_vprintf); + ASAN_INTERCEPT_FUNC(__isoc99_vfprintf); + ASAN_INTERCEPT_FUNC(__isoc99_vsprintf); + ASAN_INTERCEPT_FUNC(__isoc99_vsnprintf); +#endif + ASAN_INTERCEPT_FUNC(printf); + ASAN_INTERCEPT_FUNC(fprintf); + ASAN_INTERCEPT_FUNC(sprintf); // NOLINT + ASAN_INTERCEPT_FUNC(snprintf); + ASAN_INTERCEPT_FUNC(asprintf); +#if ASAN_INTERCEPT_FUNC_ISOC99_PRINTF + ASAN_INTERCEPT_FUNC(__isoc99_printf); + ASAN_INTERCEPT_FUNC(__isoc99_fprintf); + ASAN_INTERCEPT_FUNC(__isoc99_sprintf); + ASAN_INTERCEPT_FUNC(__isoc99_snprintf); +#endif +#endif + // Some Windows-specific interceptors. #if SANITIZER_WINDOWS InitializeWindowsInterceptors(); Index: sanitizer_common/sanitizer_common_interceptors_scanf.inc =================================================================== --- sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -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. @@ -280,7 +280,7 @@ 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 +295,68 @@ } 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) 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); } } + +// 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; + + 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) + break; + if (dir.convSpecifier == 'n') { + void *argp = va_arg(aq, void *); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + 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: + break; // TODO(ygribov): issue warning? + } + } + } +}