12d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines//===-- sanitizer_common_interceptors_format.inc ----------------*- C++ -*-===// 22d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// 32d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// The LLVM Compiler Infrastructure 42d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// 52d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// This file is distributed under the University of Illinois Open Source 62d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// License. See LICENSE.TXT for details. 72d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// 82d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines//===----------------------------------------------------------------------===// 92d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// 102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Scanf/printf implementation for use in *Sanitizer interceptors. 112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html 122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html 132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// with a few common GNU extensions. 142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// 152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines//===----------------------------------------------------------------------===// 162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#include <stdarg.h> 172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *parse_number(const char *p, int *out) { 192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines *out = internal_atoll(p); 202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p >= '0' && *p <= '9') 212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *maybe_parse_param_index(const char *p, int *out) { 262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // n$ 272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p >= '0' && *p <= '9') { 282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int number; 292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *q = parse_number(p, &number); 302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK(q); 312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*q == '$') { 322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines *out = number; 332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = q + 1; 342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Otherwise, do not change p. This will be re-parsed later as the field 382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // width. 392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic bool char_is_one_of(char c, const char *s) { 432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return !!internal_strchr(s, c); 442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *maybe_parse_length_modifier(const char *p, char ll[2]) { 472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(*p, "jztLq")) { 482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ll[0] = *p; 492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (*p == 'h') { 512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ll[0] = 'h'; 522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == 'h') { 542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ll[1] = 'h'; 552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (*p == 'l') { 582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ll[0] = 'l'; 592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == 'l') { 612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ll[1] = 'l'; 622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Returns true if the character is an integer conversion specifier. 692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic bool format_is_integer_conv(char c) { 702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return char_is_one_of(c, "diouxXn"); 712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Returns true if the character is an floating point conversion specifier. 742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic bool format_is_float_conv(char c) { 752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return char_is_one_of(c, "aAeEfFgG"); 762d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Returns string output character size for string-like conversions, 792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// or 0 if the conversion is invalid. 802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic int format_get_char_size(char convSpecifier, 812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char lengthModifier[2]) { 822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(convSpecifier, "CS")) { 832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(wchar_t); 842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(convSpecifier, "cs[")) { 872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (lengthModifier[0] == 'l' && lengthModifier[1] == '\0') 882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(wchar_t); 892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines else if (lengthModifier[0] == '\0') 902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(char); 912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesenum FormatStoreSize { 972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Store size not known in advance; can be calculated as wcslen() of the 982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // destination buffer. 992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines FSS_WCSLEN = -2, 1002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Store size not known in advance; can be calculated as strlen() of the 1012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // destination buffer. 1022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines FSS_STRLEN = -1, 1032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Invalid conversion specifier. 1042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines FSS_INVALID = 0 1052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines}; 1062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Returns the memory size of a format directive (if >0), or a value of 1082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// FormatStoreSize. 1092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic int format_get_value_size(char convSpecifier, 1102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char lengthModifier[2], 1112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool promote_float) { 1122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (format_is_integer_conv(convSpecifier)) { 1132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (lengthModifier[0]) { 1142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'h': 1152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); 1162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'l': 1172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); 1182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'q': 1192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(long long); 1202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'L': 1212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(long long); 1222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'j': 1232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(INTMAX_T); 1242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'z': 1252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(SIZE_T); 1262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 't': 1272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(PTRDIFF_T); 1282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0: 1292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(int); 1302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines default: 1312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 1322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (format_is_float_conv(convSpecifier)) { 1362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (lengthModifier[0]) { 1372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'L': 1382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'q': 1392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(long double); 1402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 'l': 1412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return lengthModifier[1] == 'l' ? sizeof(long double) 1422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines : sizeof(double); 1432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0: 1442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Printf promotes floats to doubles but scanf does not 1452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return promote_float ? sizeof(double) : sizeof(float); 1462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines default: 1472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 1482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (convSpecifier == 'p') { 1522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (lengthModifier[0] != 0) 1532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 1542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(void *); 1552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 1582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 1592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstruct ScanfDirective { 1612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int argIdx; // argument index, or -1 if not specified ("%n$") 1622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int fieldWidth; 1632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *begin; 1642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *end; 1652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool suppressed; // suppress assignment ("*") 1662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool allocate; // allocate space ("m") 1672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char lengthModifier[2]; 1682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char convSpecifier; 1692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool maybeGnuMalloc; 1702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines}; 1712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Parse scanf format string. If a valid directive in encountered, it is 1732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// returned in dir. This function returns the pointer to the first 1742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// unprocessed character, or 0 in case of error. 1752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// In case of the end-of-string, a pointer to the closing \0 is returned. 1762d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *scanf_parse_next(const char *p, bool allowGnuMalloc, 1772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ScanfDirective *dir) { 1782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines internal_memset(dir, 0, sizeof(*dir)); 1792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->argIdx = -1; 1802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p) { 1822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p != '%') { 1832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 1842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 1852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->begin = p; 1872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 1882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // %% 1892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '%') { 1902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 1912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 1922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '\0') { 1942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 1952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // %n$ 1972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_param_index(p, &dir->argIdx); 1982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK(p); 1992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // * 2002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '*') { 2012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->suppressed = true; 2022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Field width 2052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p >= '0' && *p <= '9') { 2062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = parse_number(p, &dir->fieldWidth); 2072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK(p); 2082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->fieldWidth <= 0) // Width if at all must be non-zero 2092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 2102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // m 2122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == 'm') { 2132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->allocate = true; 2142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Length modifier. 2172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_length_modifier(p, dir->lengthModifier); 2182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Conversion specifier. 2192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->convSpecifier = *p++; 2202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Consume %[...] expression. 2212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->convSpecifier == '[') { 2222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '^') 2232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == ']') 2252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p && *p != ']') 2272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == 0) 2292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; // unexpected end of string 2302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Consume the closing ']'. 2312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // This is unfortunately ambiguous between old GNU extension 2342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // of %as, %aS and %a[...] and newer POSIX %a followed by 2352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // letters s, S or [. 2362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (allowGnuMalloc && dir->convSpecifier == 'a' && 2372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines !dir->lengthModifier[0]) { 2382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == 's' || *p == 'S') { 2392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->maybeGnuMalloc = true; 2402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 2412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (*p == '[') { 2422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Watch for %a[h-j%d], if % appears in the 2432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // [...] range, then we need to give up, we don't know 2442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // if scanf will parse it as POSIX %a [h-j %d ] or 2452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // GNU allocation of string with range dh-j plus %. 2462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *q = p + 1; 2472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*q == '^') 2482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++q; 2492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*q == ']') 2502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++q; 2512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*q && *q != ']' && *q != '%') 2522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++q; 2532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*q == 0 || *q == '%') 2542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 2552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = q + 1; // Consume the closing ']'. 2562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->maybeGnuMalloc = true; 2572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->end = p; 2602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 2612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 2632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 2642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 2652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic int scanf_get_value_size(ScanfDirective *dir) { 2662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->allocate) { 2672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!char_is_one_of(dir->convSpecifier, "cCsS[")) 2682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 2692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(char *); 2702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 2722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->maybeGnuMalloc) { 2732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->convSpecifier != 'a' || dir->lengthModifier[0]) 2742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 2752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // This is ambiguous, so check the smaller size of char * (if it is 2762d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // a GNU extension of %as, %aS or %a[...]) and float (if it is 2772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // POSIX %a followed by s, S or [ letters). 2782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float); 2792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 2812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(dir->convSpecifier, "cCsS[")) { 2822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool needsTerminator = char_is_one_of(dir->convSpecifier, "sS["); 2832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines unsigned charSize = 2842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines format_get_char_size(dir->convSpecifier, dir->lengthModifier); 2852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (charSize == 0) 2862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 2872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->fieldWidth == 0) { 2882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!needsTerminator) 2892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return charSize; 2902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN; 2912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return (dir->fieldWidth + needsTerminator) * charSize; 2932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 2942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 2952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return format_get_value_size(dir->convSpecifier, dir->lengthModifier, false); 2962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 2972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 2982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Common part of *scanf interceptors. 2992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Process format string and va_list, and report all store ranges. 3002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Stops when "consuming" n_inputs input items. 3012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, 3022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *format, va_list aq) { 3032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK_GT(n_inputs, 0); 3042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *p = format; 3052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1); 3072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p) { 3092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ScanfDirective dir; 3102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = scanf_parse_next(p, allowGnuMalloc, &dir); 3112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!p) 3122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 3132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.convSpecifier == 0) { 3142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // This can only happen at the end of the format string. 3152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK_EQ(*p, 0); 3162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 3172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Here the directive is valid. Do what it says. 3192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.argIdx != -1) { 3202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Unsupported. 3212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 3222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.suppressed) 3242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 3252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int size = scanf_get_value_size(&dir); 3262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (size == FSS_INVALID) { 3272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines Report("WARNING: unexpected format specifier in scanf interceptor: " 3282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines "%.*s\n", dir.end - dir.begin, dir.begin); 3292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 3302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines void *argp = va_arg(aq, void *); 3322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.convSpecifier != 'n') 3332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines --n_inputs; 3342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (n_inputs < 0) 3352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 3362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (size == FSS_STRLEN) { 3372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = internal_strlen((const char *)argp) + 1; 3382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (size == FSS_WCSLEN) { 3392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // FIXME: actually use wcslen() to calculate it. 3402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = 0; 3412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); 3432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 3452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#if SANITIZER_INTERCEPT_PRINTF 3472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstruct PrintfDirective { 3492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int fieldWidth; 3502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int fieldPrecision; 3512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int argIdx; // width argument index, or -1 if not specified ("%*n$") 3522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int precisionIdx; // precision argument index, or -1 if not specified (".*n$") 3532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *begin; 3542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *end; 3552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool starredWidth; 3562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool starredPrecision; 3572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char lengthModifier[2]; 3582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char convSpecifier; 3592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines}; 3602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *maybe_parse_number(const char *p, int *out) { 3622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p >= '0' && *p <= '9') 3632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = parse_number(p, out); 3642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 3652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 3662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *maybe_parse_number_or_star(const char *p, int *out, 3682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines bool *star) { 3692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '*') { 3702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines *star = true; 3712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 3722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else { 3732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines *star = false; 3742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_number(p, out); 3752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3762d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 3772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 3782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Parse printf format string. Same as scanf_parse_next. 3802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic const char *printf_parse_next(const char *p, PrintfDirective *dir) { 3812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines internal_memset(dir, 0, sizeof(*dir)); 3822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->argIdx = -1; 3832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->precisionIdx = -1; 3842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 3852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p) { 3862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p != '%') { 3872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 3882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 3892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->begin = p; 3912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 3922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // %% 3932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '%') { 3942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 3952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 3962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 3972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '\0') { 3982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 3992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // %n$ 4012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_param_index(p, &dir->precisionIdx); 4022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK(p); 4032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Flags 4042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (char_is_one_of(*p, "'-+ #0")) { 4052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 4062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Field width 4082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_number_or_star(p, &dir->fieldWidth, 4092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines &dir->starredWidth); 4102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!p) 4112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 4122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Precision 4132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (*p == '.') { 4142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines ++p; 4152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Actual precision is optional (surprise!) 4162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_number_or_star(p, &dir->fieldPrecision, 4172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines &dir->starredPrecision); 4182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!p) 4192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 4202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // m$ 4212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->starredPrecision) { 4222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_param_index(p, &dir->precisionIdx); 4232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK(p); 4242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Length modifier. 4272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = maybe_parse_length_modifier(p, dir->lengthModifier); 4282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Conversion specifier. 4292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->convSpecifier = *p++; 4302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines dir->end = p; 4312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 4322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return p; 4342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 4352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic int printf_get_value_size(PrintfDirective *dir) { 4372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir->convSpecifier == 'm') { 4382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return sizeof(char *); 4392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(dir->convSpecifier, "cCsS")) { 4422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines unsigned charSize = 4432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines format_get_char_size(dir->convSpecifier, dir->lengthModifier); 4442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (charSize == 0) 4452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return FSS_INVALID; 4462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (char_is_one_of(dir->convSpecifier, "sS")) { 4472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN; 4482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return charSize; 4502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 4512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return format_get_value_size(dir->convSpecifier, dir->lengthModifier, true); 4532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 4542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#define SKIP_SCALAR_ARG(aq, convSpecifier, size) \ 4562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines do { \ 4572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (format_is_float_conv(convSpecifier)) { \ 4582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (size) { \ 4592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 8: \ 4602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines va_arg(*aq, double); \ 4612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; \ 4622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 12: \ 4632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines va_arg(*aq, long double); \ 4642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; \ 4652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 16: \ 4662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines va_arg(*aq, long double); \ 4672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; \ 4682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines default: \ 4692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines Report("WARNING: unexpected floating-point arg size" \ 4702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines " in printf interceptor: %d\n", size); \ 4712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return; \ 4722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } \ 4732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else { \ 4742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (size) { \ 4752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 1: \ 4762d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 2: \ 4772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 4: \ 4782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines va_arg(*aq, u32); \ 4792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; \ 4802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 8: \ 4812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines va_arg(*aq, u64); \ 4822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; \ 4832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines default: \ 4842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines Report("WARNING: unexpected arg size" \ 4852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines " in printf interceptor: %d\n", size); \ 4862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return; \ 4872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } \ 4882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } \ 4892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } while (0) 4902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4912d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Common part of *printf interceptors. 4922d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Process format string and va_list, and report all load ranges. 4932d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic void printf_common(void *ctx, const char *format, va_list aq) { 4942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1); 4952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines const char *p = format; 4972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 4982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (*p) { 4992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines PrintfDirective dir; 5002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines p = printf_parse_next(p, &dir); 5012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!p) 5022d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 5032d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.convSpecifier == 0) { 5042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // This can only happen at the end of the format string. 5052d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines CHECK_EQ(*p, 0); 5062d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 5072d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Here the directive is valid. Do what it says. 5092d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.argIdx != -1 || dir.precisionIdx != -1) { 5102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Unsupported. 5112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 5122d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.starredWidth) { 5142d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Dynamic width 5152d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines SKIP_SCALAR_ARG(&aq, 'd', sizeof(int)); 5162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.starredPrecision) { 5182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Dynamic precision 5192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines SKIP_SCALAR_ARG(&aq, 'd', sizeof(int)); 5202d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5212d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines int size = printf_get_value_size(&dir); 5222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (size == FSS_INVALID) { 5232d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines Report("WARNING: unexpected format specifier in printf " 5242d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines "interceptor: %.*s\n", dir.end - dir.begin, dir.begin); 5252d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines break; 5262d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.convSpecifier == 'n') { 5282d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines void *argp = va_arg(aq, void *); 5292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); 5302d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 5312d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (size == FSS_STRLEN) { 5322d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (void *argp = va_arg(aq, void *)) { 5332d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (dir.starredPrecision) { 5342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // FIXME: properly support starred precision for strings. 5352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = 0; 5362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (dir.fieldPrecision > 0) { 5372d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Won't read more than "precision" symbols. 5382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = internal_strnlen((const char *)argp, dir.fieldPrecision); 5392d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (size < dir.fieldPrecision) size++; 5402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else { 5412d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Whole string will be accessed. 5422d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = internal_strlen((const char *)argp) + 1; 5432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size); 5452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else if (size == FSS_WCSLEN) { 5472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (void *argp = va_arg(aq, void *)) { 5482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // FIXME: Properly support wide-character strings (via wcsrtombs). 5492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size = 0; 5502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size); 5512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } else { 5532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Skip non-pointer args 5542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size); 5552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 5572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 5582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 5592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#endif // SANITIZER_INTERCEPT_PRINTF 560