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