sanitizer_common_interceptors_scanf.inc   [plain text]


//===-- sanitizer_common_interceptors_scanf.inc -----------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Scanf implementation for use in *Sanitizer interceptors.
//
//===----------------------------------------------------------------------===//
#include <stdarg.h>

#ifdef _WIN32
#define va_copy(dst, src) ((dst) = (src))
#endif  // _WIN32

struct ScanfSpec {
  char c;
  unsigned size;
};

// One-letter specs.
static const ScanfSpec scanf_specs[] = {
  {'s', 1},  // FIXME: This is incorrect, we should check the actual number
             // of bytes written to the string.
  {'c', sizeof(char)},
  {'p', sizeof(void *)},
  {'e', sizeof(float)},
  {'E', sizeof(float)},
  {'a', sizeof(float)},
  {'f', sizeof(float)},
  {'g', sizeof(float)},
  {'d', sizeof(int)},
  {'i', sizeof(int)},
  {'o', sizeof(int)},
  {'u', sizeof(int)},
  {'x', sizeof(int)},
  {'X', sizeof(int)},
  {'n', sizeof(int)},
  {'t', sizeof(PTRDIFF_T)},
  {'z', sizeof(SIZE_T)},
  {'j', sizeof(INTMAX_T)},
  {'h', sizeof(short)}
};

static const unsigned scanf_specs_cnt =
  sizeof(scanf_specs) / sizeof(scanf_specs[0]);

// %ll?, %L?, %q? specs
static const ScanfSpec scanf_llspecs[] = {
  {'e', sizeof(long double)},
  {'f', sizeof(long double)},
  {'g', sizeof(long double)},
  {'d', sizeof(long long)},
  {'i', sizeof(long long)},
  {'o', sizeof(long long)},
  {'u', sizeof(long long)},
  {'x', sizeof(long long)}
};

static const unsigned scanf_llspecs_cnt =
  sizeof(scanf_llspecs) / sizeof(scanf_llspecs[0]);

// %l? specs
static const ScanfSpec scanf_lspecs[] = {
  {'e', sizeof(double)},
  {'f', sizeof(double)},
  {'g', sizeof(double)},
  {'d', sizeof(long)},
  {'i', sizeof(long)},
  {'o', sizeof(long)},
  {'u', sizeof(long)},
  {'x', sizeof(long)},
  {'X', sizeof(long)},
};

static const unsigned scanf_lspecs_cnt =
  sizeof(scanf_lspecs) / sizeof(scanf_lspecs[0]);

static unsigned match_spec(const struct ScanfSpec *spec, unsigned n, char c) {
  for (unsigned i = 0; i < n; ++i)
    if (spec[i].c == c)
      return spec[i].size;
  return 0;
}

static void scanf_common(void *ctx, const char *format, va_list ap_const) {
  va_list aq;
  va_copy(aq, ap_const);

  const char *p = format;
  unsigned size;

  while (*p) {
    if (*p != '%') {
      ++p;
      continue;
    }
    ++p;
    if (*p == '*' || *p == '%' || *p == '\0') {
      // FIXME: Bailing out for (p == "*") is wrong, we should parse the
      // directive to the end.
      if (*p != '\0')
        ++p;
      continue;
    }

    unsigned field_width = 0;
    if (*p >= '0' && *p <= '9') {
      field_width = internal_atoll(p);
      while (*p >= '0' && *p <= '9')
        p++;
    }
    if (field_width > 0) {
      // +1 for the \0 at the end.
      if (*p == 's')
        field_width++;
      if (*p == 's' || *p == 'c') {
        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void*), field_width);
        ++p;
        continue;
      }
    }

    if (*p == '[') {
      // Search for the closing bracket. It is ignored if it goes right after
      // the opening bracket or after ^.
      p++;
      if (*p == ']') {
        p++;
      } else if (*p == '^' && *(p+1) == ']') {
        p += 2;
      }
      while (*p != ']')
        p++;
      // +1 for the \0 at the end.
      field_width++;
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void*), field_width);
      continue;
    }

    if (*p == 'L' || *p == 'q') {
      ++p;
      size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
      continue;
    }

    if (*p == 'l') {
      ++p;
      if (*p == 'l') {
        ++p;
        size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
        continue;
      } else {
        size = match_spec(scanf_lspecs, scanf_lspecs_cnt, *p);
        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
        continue;
      }
    }

    if (*p == 'h' && *(p + 1) == 'h') {
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), sizeof(char));
      p += 2;
      continue;
    }

    size = match_spec(scanf_specs, scanf_specs_cnt, *p);
    if (size) {
      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
      ++p;
      continue;
    }
  }
  va_end(aq);
}