[compiler-rt] r199729 - Sanitize printf functions.

Alexey Samsonov samsonov at google.com
Tue Jan 21 03:58:33 PST 2014


Author: samsonov
Date: Tue Jan 21 05:58:33 2014
New Revision: 199729

URL: http://llvm.org/viewvc/llvm-project?rev=199729&view=rev
Log:
Sanitize printf functions.

Intercept and sanitize arguments passed to printf functions in ASan and TSan
(don't do this in MSan for now). The checks are controlled by runtime flag
(off by default for now).

Patch http://llvm-reviews.chandlerc.com/D2480 by Yuri Gribov!

Added:
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-1.c
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-2.c
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-3.c
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-4.c
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-5.c
    compiler-rt/trunk/lib/tsan/lit_tests/printf-1.c
Modified:
    compiler-rt/trunk/lib/msan/msan_interceptors.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h
    compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_stat.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_stat.h

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-1.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-1.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-1.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-1.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,16 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s
+// RUN: %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  printf("%c %d %.3f %s\n", c, x, f, s);
+  return 0;
+  // Check that printf works fine under Asan.
+  // CHECK: 0 12 1.239 34
+}

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-2.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-2.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-2.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-2.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,22 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  char *p = strdup((const char *)s);
+  free(p);
+  printf("%c %d %.3f %s\n", c, x, f, p);
+  return 0;
+  // Check that %s is sanitized.
+  // CHECK-ON: heap-use-after-free
+  // CHECK-ON-NOT: 0 12 1.239 34
+  // CHECK-OFF: 0 12 1.239 
+}

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-3.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-3.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-3.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-3.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,19 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+
+#include <stdio.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  volatile int n[1];
+  printf("%c %d %.3f %s%n\n", c, x, f, s, &n[1]);
+  return 0;
+  // Check that %n is sanitized.
+  // CHECK-ON: stack-buffer-overflow
+  // CHECK-ON-NOT: 0 12 1.239 34
+  // CHECK-OFF: 0 12 1.239 34
+}

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-4.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-4.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-4.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-4.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,20 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+
+#include <stdio.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  volatile char buf[2];
+  sprintf((char *)buf, "%c %d %.3f %s\n", c, x, f, s);
+  puts((const char *)buf);
+  return 0;
+  // Check that size of output buffer is sanitized.
+  // CHECK-ON: stack-buffer-overflow
+  // CHECK-ON-NOT: 0 12 1.239 34
+  // CHECK-OFF: 0 12 1.239 34
+}

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-5.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-5.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-5.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/printf-5.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,21 @@
+// RUN: %clang_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
+
+#include <stdio.h>
+#include <string.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  volatile char fmt[2];
+  memcpy((char *)fmt, "%c %d %f %s\n", sizeof(fmt));
+  printf(fmt, c, x, f, s);
+  return 0;
+  // Check that format string is sanitized.
+  // CHECK-ON: stack-buffer-overflow
+  // CHECK-ON-NOT: 0 12 1.239 34
+  // CHECK-OFF: 0
+}

Modified: compiler-rt/trunk/lib/msan/msan_interceptors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/msan/msan_interceptors.cc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/msan/msan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/msan/msan_interceptors.cc Tue Jan 21 05:58:33 2014
@@ -1291,6 +1291,8 @@ extern "C" int *__errno_location(void);
   } while (false)  // FIXME
 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
+// FIXME: update Msan to use common printf interceptors
+#define SANITIZER_INTERCEPT_PRINTF 0
 #include "sanitizer_common/sanitizer_common_interceptors.inc"
 
 #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc Tue Jan 21 05:58:33 2014
@@ -569,10 +569,24 @@ INTERCEPTOR(char *, strptime, char *s, c
 #define INIT_STRPTIME
 #endif
 
-#if SANITIZER_INTERCEPT_SCANF
-
+#if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF
 #include "sanitizer_common_interceptors_scanf.inc"
 
+#define FORMAT_INTERCEPTOR_IMPL(name, vname, ...)                              \
+  {                                                                            \
+    void *ctx;                                                                 \
+    va_list ap;                                                                \
+    va_start(ap, format);                                                      \
+    COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap);                     \
+    int res = vname(__VA_ARGS__, ap);                                          \
+    va_end(ap);                                                                \
+    return res;                                                                \
+  }
+
+#endif
+
+#if SANITIZER_INTERCEPT_SCANF
+
 #define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...)                    \
   {                                                                            \
     void *ctx;                                                                 \
@@ -607,35 +621,24 @@ INTERCEPTOR(int, __isoc99_vfscanf, void
 VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap)
 #endif  // SANITIZER_INTERCEPT_ISOC99_SCANF
 
-#define SCANF_INTERCEPTOR_IMPL(name, vname, ...)                               \
-  {                                                                            \
-    void *ctx;                                                                 \
-    va_list ap;                                                                \
-    va_start(ap, format);                                                      \
-    COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap);                     \
-    int res = vname(__VA_ARGS__, ap);                                          \
-    va_end(ap);                                                                \
-    return res;                                                                \
-  }
-
 INTERCEPTOR(int, scanf, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format)
+FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format)
 
 INTERCEPTOR(int, fscanf, void *stream, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format)
+FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format)
 
 INTERCEPTOR(int, sscanf, const char *str, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format)
+FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format)
 
 #if SANITIZER_INTERCEPT_ISOC99_SCANF
 INTERCEPTOR(int, __isoc99_scanf, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format)
 
 INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
 
 INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
 #endif
 
 #endif
@@ -664,6 +667,167 @@ SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf,
 #define INIT_ISOC99_SCANF
 #endif
 
+#if SANITIZER_INTERCEPT_PRINTF
+
+#define VPRINTF_INTERCEPTOR_ENTER(vname, ...)                                  \
+  void *ctx;                                                                   \
+  COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__);                           \
+  va_list aq;                                                                  \
+  va_copy(aq, ap);
+
+#define VPRINTF_INTERCEPTOR_RETURN()                                           \
+  va_end(aq);
+
+#define VPRINTF_INTERCEPTOR_IMPL(vname, ...)                                   \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__);                             \
+    if (common_flags()->check_printf)                                          \
+      printf_common(ctx, format, aq);                                          \
+    int res = REAL(vname)(__VA_ARGS__);                                        \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+#define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...)                             \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__)                         \
+    if (common_flags()->check_printf) {                                        \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(str, __VA_ARGS__);                                   \
+    if (res >= 0 && common_flags()->check_printf) {                            \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1);                       \
+    }                                                                          \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+#define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...)                      \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__)                   \
+    if (common_flags()->check_printf) {                                        \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, size);                          \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(str, size, __VA_ARGS__);                             \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+#define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...)                           \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__)                        \
+    if (common_flags()->check_printf) {                                        \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *));               \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(strp, __VA_ARGS__);                                  \
+    if (res >= 0 && common_flags()->check_printf) {                            \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1);                     \
+    }                                                                          \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+INTERCEPTOR(int, vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap)
+
+INTERCEPTOR(int, vfprintf, void *stream, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format,
+            va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap)
+
+INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap)
+
+INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap)
+VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap)
+
+INTERCEPTOR(int, __isoc99_vfprintf, void *stream, const char *format,
+            va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format,
+            va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format,
+            va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format,
+                          ap)
+
+#endif  // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+INTERCEPTOR(int, printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format)
+
+INTERCEPTOR(int, fprintf, void *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format)
+
+INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT
+FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT
+
+INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format)
+
+INTERCEPTOR(int, asprintf, char **strp, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format)
+
+INTERCEPTOR(int, __isoc99_fprintf, void *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format)
+
+INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format)
+
+INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size,
+            const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
+                        format)
+
+#endif  // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+#endif  // SANITIZER_INTERCEPT_PRINTF
+
+#if SANITIZER_INTERCEPT_PRINTF
+#define INIT_PRINTF                     \
+  COMMON_INTERCEPT_FUNCTION(printf);    \
+  COMMON_INTERCEPT_FUNCTION(sprintf);   \
+  COMMON_INTERCEPT_FUNCTION(snprintf);  \
+  COMMON_INTERCEPT_FUNCTION(asprintf);  \
+  COMMON_INTERCEPT_FUNCTION(fprintf);   \
+  COMMON_INTERCEPT_FUNCTION(vprintf);   \
+  COMMON_INTERCEPT_FUNCTION(vsprintf);  \
+  COMMON_INTERCEPT_FUNCTION(vsnprintf); \
+  COMMON_INTERCEPT_FUNCTION(vasprintf); \
+  COMMON_INTERCEPT_FUNCTION(vfprintf);
+#else
+#define INIT_PRINTF
+#endif
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+#define INIT_ISOC99_PRINTF                       \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_printf);    \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf);
+#else
+#define INIT_ISOC99_PRINTF
+#endif
+
 #if SANITIZER_INTERCEPT_IOCTL
 #include "sanitizer_common_interceptors_ioctl.inc"
 INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
@@ -2924,6 +3088,8 @@ INTERCEPTOR(__sanitizer_clock_t, times,
   INIT_STRPTIME;                           \
   INIT_SCANF;                              \
   INIT_ISOC99_SCANF;                       \
+  INIT_PRINTF;                             \
+  INIT_ISOC99_PRINTF;                      \
   INIT_FREXP;                              \
   INIT_FREXPF_FREXPL;                      \
   INIT_GETPWNAM_AND_FRIENDS;               \

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc Tue Jan 21 05:58:33 2014
@@ -7,23 +7,14 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Scanf implementation for use in *Sanitizer interceptors.
+// Scanf/printf implementation for use in *Sanitizer interceptors.
 // Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html
+// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
 // with a few common GNU extensions.
 //
 //===----------------------------------------------------------------------===//
 #include <stdarg.h>
 
-struct ScanfDirective {
-  int argIdx; // argument index, or -1 of not specified ("%n$")
-  int fieldWidth;
-  bool suppressed; // suppress assignment ("*")
-  bool allocate;   // allocate space ("m")
-  char lengthModifier[2];
-  char convSpecifier;
-  bool maybeGnuMalloc;
-};
-
 static const char *parse_number(const char *p, int *out) {
   *out = internal_atoll(p);
   while (*p >= '0' && *p <= '9')
@@ -31,10 +22,179 @@ static const char *parse_number(const ch
   return p;
 }
 
+static const char *maybe_parse_number(const char *p, int *out) {
+  if (*p >= '0' && *p <= '9')
+    p = parse_number(p, out);
+  return p;
+}
+
+static const char *maybe_parse_number_or_star(const char *p, int *out,
+                                              bool *star) {
+  if (*p == '*') {
+    *star = true;
+    ++p;
+  } else {
+    *star = false;
+    p = maybe_parse_number(p, out);
+  }
+  return p;
+}
+
+static const char *maybe_parse_param_index(const char *p, int *out) {
+  // n$
+  if (*p >= '0' && *p <= '9') {
+    int number;
+    const char *q = parse_number(p, &number);
+    CHECK(q);
+    if (*q == '$') {
+      *out = number;
+      p = q + 1;
+    }
+  }
+
+  // Otherwise, do not change p. This will be re-parsed later as the field
+  // width.
+  return p;
+}
+
 static bool char_is_one_of(char c, const char *s) {
   return !!internal_strchr(s, c);
 }
 
+static const char *maybe_parse_length_modifier(const char *p, char ll[2]) {
+  if (char_is_one_of(*p, "jztLq")) {
+    ll[0] = *p;
+    ++p;
+  } else if (*p == 'h') {
+    ll[0] = 'h';
+    ++p;
+    if (*p == 'h') {
+      ll[1] = 'h';
+      ++p;
+    }
+  } else if (*p == 'l') {
+    ll[0] = 'l';
+    ++p;
+    if (*p == 'l') {
+      ll[1] = 'l';
+      ++p;
+    }
+  }
+  return p;
+}
+
+// Returns true if the character is an integer conversion specifier.
+static bool format_is_integer_conv(char c) {
+  return char_is_one_of(c, "diouxXn");
+}
+
+// Returns true if the character is an floating point conversion specifier.
+static bool format_is_float_conv(char c) {
+  return char_is_one_of(c, "aAeEfFgG");
+}
+
+// Returns string output character size for string-like conversions,
+// or 0 if the conversion is invalid.
+static int format_get_char_size(char convSpecifier,
+                                const char lengthModifier[2]) {
+  if (char_is_one_of(convSpecifier, "CS")) {
+    // wchar_t
+    return 0;
+  }
+
+  if (char_is_one_of(convSpecifier, "cs[")) {
+    if (lengthModifier[0] == 'l')
+      // wchar_t
+      return 0;
+    else if (lengthModifier[0] == 0)
+      return sizeof(char);
+    else
+      return 0;
+  }
+
+  return 0;
+}
+
+enum FormatStoreSize {
+  // Store size not known in advance; can be calculated as strlen() of the
+  // destination buffer.
+  FSS_STRLEN = -1,
+  // Invalid conversion specifier.
+  FSS_INVALID = 0
+};
+
+// Returns the memory size of a format directive (if >0), or a value of
+// FormatStoreSize.
+static int format_get_value_size(char convSpecifier,
+                                 const char lengthModifier[2], int fieldWidth,
+                                 bool promote_float) {
+  if (format_is_integer_conv(convSpecifier)) {
+    switch (lengthModifier[0]) {
+    case 'h':
+      return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
+    case 'l':
+      return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
+    case 'L':
+      return sizeof(long long);
+    case 'j':
+      return sizeof(INTMAX_T);
+    case 'z':
+      return sizeof(SIZE_T);
+    case 't':
+      return sizeof(PTRDIFF_T);
+    case 0:
+      return sizeof(int);
+    default:
+      return FSS_INVALID;
+    }
+  }
+
+  if (format_is_float_conv(convSpecifier)) {
+    switch (lengthModifier[0]) {
+    case 'L':
+    case 'q':
+      return sizeof(long double);
+    case 'l':
+      return lengthModifier[1] == 'l' ? sizeof(long double)
+                                           : sizeof(double);
+    case 0:
+      // Printf promotes floats to doubles but scanf does not
+      return promote_float ? sizeof(double) : sizeof(float);
+    default:
+      return FSS_INVALID;
+    }
+  }
+
+  if (char_is_one_of(convSpecifier, "cC")) {
+    unsigned charSize = format_get_char_size(convSpecifier, lengthModifier);
+    if (charSize == 0)
+      return FSS_INVALID;
+    if (fieldWidth == 0)
+      return charSize;
+    return fieldWidth * charSize;
+  }
+
+  if (convSpecifier == 'p') {
+    if (lengthModifier[0] != 0)
+      return FSS_INVALID;
+    return sizeof(void *);
+  }
+
+  return FSS_INVALID;
+}
+
+struct ScanfDirective {
+  int argIdx; // argument index, or -1 if not specified ("%n$")
+  int fieldWidth;
+  const char *begin;
+  const char *end;
+  bool suppressed; // suppress assignment ("*")
+  bool allocate;   // allocate space ("m")
+  char lengthModifier[2];
+  char convSpecifier;
+  bool maybeGnuMalloc;
+};
+
 // Parse scanf format string. If a valid directive in encountered, it is
 // returned in dir. This function returns the pointer to the first
 // unprocessed character, or 0 in case of error.
@@ -49,6 +209,7 @@ static const char *scanf_parse_next(cons
       ++p;
       continue;
     }
+    dir->begin = p;
     ++p;
     // %%
     if (*p == '%') {
@@ -59,25 +220,18 @@ static const char *scanf_parse_next(cons
       return 0;
     }
     // %n$
-    if (*p >= '0' && *p <= '9') {
-      int number;
-      const char *q = parse_number(p, &number);
-      if (*q == '$') {
-        dir->argIdx = number;
-        p = q + 1;
-      }
-      // Otherwise, do not change p. This will be re-parsed later as the field
-      // width.
-    }
+    p = maybe_parse_param_index(p, &dir->argIdx);
+    CHECK(p);
     // *
     if (*p == '*') {
       dir->suppressed = true;
       ++p;
     }
-    // Field width.
+    // Field width
     if (*p >= '0' && *p <= '9') {
       p = parse_number(p, &dir->fieldWidth);
-      if (dir->fieldWidth <= 0)
+      CHECK(p);
+      if (dir->fieldWidth <= 0)  // Width if at all must be non-zero
         return 0;
     }
     // m
@@ -86,24 +240,7 @@ static const char *scanf_parse_next(cons
       ++p;
     }
     // Length modifier.
-    if (char_is_one_of(*p, "jztLq")) {
-      dir->lengthModifier[0] = *p;
-      ++p;
-    } else if (*p == 'h') {
-      dir->lengthModifier[0] = 'h';
-      ++p;
-      if (*p == 'h') {
-        dir->lengthModifier[1] = 'h';
-        ++p;
-      }
-    } else if (*p == 'l') {
-      dir->lengthModifier[0] = 'l';
-      ++p;
-      if (*p == 'l') {
-        dir->lengthModifier[1] = 'l';
-        ++p;
-      }
-    }
+    p = maybe_parse_length_modifier(p, dir->lengthModifier);
     // Conversion specifier.
     dir->convSpecifier = *p++;
     // Consume %[...] expression.
@@ -145,129 +282,40 @@ static const char *scanf_parse_next(cons
         dir->maybeGnuMalloc = true;
       }
     }
+    dir->end = p;
     break;
   }
   return p;
 }
 
-// Returns true if the character is an integer conversion specifier.
-static bool scanf_is_integer_conv(char c) {
-  return char_is_one_of(c, "diouxXn");
-}
-
-// Returns true if the character is an floating point conversion specifier.
-static bool scanf_is_float_conv(char c) {
-  return char_is_one_of(c, "aAeEfFgG");
-}
-
-// Returns string output character size for string-like conversions,
-// or 0 if the conversion is invalid.
-static int scanf_get_char_size(ScanfDirective *dir) {
-  if (char_is_one_of(dir->convSpecifier, "CS")) {
-    // wchar_t
-    return 0;
-  }
-
-  if (char_is_one_of(dir->convSpecifier, "cs[")) {
-    if (dir->lengthModifier[0] == 'l')
-      // wchar_t
-      return 0;
-    else if (dir->lengthModifier[0] == 0)
-      return sizeof(char);
-    else
-      return 0;
-  }
-
-  return 0;
-}
-
-enum ScanfStoreSize {
-  // Store size not known in advance; can be calculated as strlen() of the
-  // destination buffer.
-  SSS_STRLEN = -1,
-  // Invalid conversion specifier.
-  SSS_INVALID = 0
-};
-
-// Returns the store size of a scanf directive (if >0), or a value of
-// ScanfStoreSize.
-static int scanf_get_store_size(ScanfDirective *dir) {
+static int scanf_get_value_size(ScanfDirective *dir) {
   if (dir->allocate) {
     if (!char_is_one_of(dir->convSpecifier, "cCsS["))
-      return SSS_INVALID;
+      return FSS_INVALID;
     return sizeof(char *);
   }
 
   if (dir->maybeGnuMalloc) {
     if (dir->convSpecifier != 'a' || dir->lengthModifier[0])
-      return SSS_INVALID;
+      return FSS_INVALID;
     // This is ambiguous, so check the smaller size of char * (if it is
     // a GNU extension of %as, %aS or %a[...]) and float (if it is
     // POSIX %a followed by s, S or [ letters).
     return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float);
   }
 
-  if (scanf_is_integer_conv(dir->convSpecifier)) {
-    switch (dir->lengthModifier[0]) {
-    case 'h':
-      return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
-    case 'l':
-      return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
-    case 'L':
-      return sizeof(long long);
-    case 'j':
-      return sizeof(INTMAX_T);
-    case 'z':
-      return sizeof(SIZE_T);
-    case 't':
-      return sizeof(PTRDIFF_T);
-    case 0:
-      return sizeof(int);
-    default:
-      return SSS_INVALID;
-    }
-  }
-
-  if (scanf_is_float_conv(dir->convSpecifier)) {
-    switch (dir->lengthModifier[0]) {
-    case 'L':
-    case 'q':
-      return sizeof(long double);
-    case 'l':
-      return dir->lengthModifier[1] == 'l' ? sizeof(long double)
-                                           : sizeof(double);
-    case 0:
-      return sizeof(float);
-    default:
-      return SSS_INVALID;
-    }
-  }
-
   if (char_is_one_of(dir->convSpecifier, "sS[")) {
-    unsigned charSize = scanf_get_char_size(dir);
+    unsigned charSize = format_get_char_size(dir->convSpecifier,
+                                             dir->lengthModifier);
     if (charSize == 0)
-      return SSS_INVALID;
+      return FSS_INVALID;
     if (dir->fieldWidth == 0)
-      return SSS_STRLEN;
+      return FSS_STRLEN;
     return (dir->fieldWidth + 1) * charSize;
   }
 
-  if (char_is_one_of(dir->convSpecifier, "cC")) {
-    unsigned charSize = scanf_get_char_size(dir);
-    if (charSize == 0)
-      return SSS_INVALID;
-    if (dir->fieldWidth == 0)
-      return charSize;
-    return dir->fieldWidth * charSize;
-  }
-
-  if (dir->convSpecifier == 'p') {
-    if (dir->lengthModifier[1] != 0)
-      return SSS_INVALID;
-    return sizeof(void *);
-  }
-
-  return SSS_INVALID;
+  return format_get_value_size(dir->convSpecifier, dir->lengthModifier,
+                               dir->fieldWidth, false);
 }
 
 // Common part of *scanf interceptors.
@@ -278,6 +326,8 @@ static void scanf_common(void *ctx, int
   CHECK_GT(n_inputs, 0);
   const char *p = format;
 
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
   while (*p) {
     ScanfDirective dir;
     p = scanf_parse_next(p, allowGnuMalloc, &dir);
@@ -295,17 +345,191 @@ static void scanf_common(void *ctx, int
     }
     if (dir.suppressed)
       continue;
-    int size = scanf_get_store_size(&dir);
-    if (size == SSS_INVALID)
+    int size = scanf_get_value_size(&dir);
+    if (size == FSS_INVALID) {
+      Report("WARNING: unexpected format specifier in scanf interceptor: "
+        "%.*s\n", dir.end - dir.begin, dir.begin);
       break;
+    }
     void *argp = va_arg(aq, void *);
     if (dir.convSpecifier != 'n')
       --n_inputs;
     if (n_inputs < 0)
       break;
-    if (size == SSS_STRLEN) {
+    if (size == FSS_STRLEN) {
       size = internal_strlen((const char *)argp) + 1;
     }
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
   }
 }
+
+struct PrintfDirective {
+  int fieldWidth;
+  int fieldPrecision;
+  int argIdx; // width argument index, or -1 if not specified ("%*n$")
+  int precisionIdx; // precision argument index, or -1 if not specified (".*n$")
+  const char *begin;
+  const char *end;
+  bool starredWidth;
+  bool starredPrecision;
+  char lengthModifier[2];
+  char convSpecifier;
+};
+
+// Parse printf format string. Same as scanf_parse_next.
+static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
+  internal_memset(dir, 0, sizeof(*dir));
+  dir->argIdx = dir->precisionIdx = -1;
+
+  while (*p) {
+    if (*p != '%') {
+      ++p;
+      continue;
+    }
+    dir->begin = p;
+    ++p;
+    // %%
+    if (*p == '%') {
+      ++p;
+      continue;
+    }
+    if (*p == '\0') {
+      return 0;
+    }
+    // %n$
+    p = maybe_parse_param_index(p, &dir->precisionIdx);
+    CHECK(p);
+    // Flags
+    while (char_is_one_of(*p, "'-+ #0")) {
+      ++p;
+    }
+    // Field width
+    p = maybe_parse_number_or_star(p, &dir->fieldWidth,
+                                   &dir->starredWidth);
+    if (!p)
+      return 0;
+    // Precision
+    if (*p == '.') {
+      ++p;
+      // Actual precision is optional (surprise!)
+      p = maybe_parse_number_or_star(p, &dir->fieldPrecision,
+                                     &dir->starredPrecision);
+      if (!p)
+        return 0;
+      // m$
+      if (dir->starredPrecision) {
+        p = maybe_parse_param_index(p, &dir->precisionIdx);
+        CHECK(p);
+      }
+    }
+    // Length modifier.
+    p = maybe_parse_length_modifier(p, dir->lengthModifier);
+    // Conversion specifier.
+    dir->convSpecifier = *p++;
+    dir->end = p;
+    break;
+  }
+  return p;
+}
+
+static int printf_get_value_size(PrintfDirective *dir) {
+  if (dir->convSpecifier == 'm') {
+    return sizeof(char *);
+  }
+
+  if (char_is_one_of(dir->convSpecifier, "sS")) {
+    unsigned charSize = format_get_char_size(dir->convSpecifier,
+                                             dir->lengthModifier);
+    if (charSize == 0)
+      return FSS_INVALID;
+    return FSS_STRLEN;
+  }
+
+  return format_get_value_size(dir->convSpecifier, dir->lengthModifier,
+                               dir->fieldWidth, true);
+}
+
+#define SKIP_SCALAR_ARG(aq, convSpecifier, size)                   \
+  do {                                                             \
+    if (format_is_float_conv(convSpecifier)) {                     \
+      switch (size) {                                              \
+      case 8:                                                      \
+        va_arg(*aq, double);                                       \
+        break;                                                     \
+      case 16:                                                     \
+        va_arg(*aq, long double);                                  \
+        break;                                                     \
+      default:                                                     \
+        Report("WARNING: unexpected floating-point arg size"       \
+               " in printf interceptor: %d\n", size);              \
+        return;                                                    \
+      }                                                            \
+    } else {                                                       \
+      switch (size) {                                              \
+      case 1:                                                      \
+      case 2:                                                      \
+      case 4:                                                      \
+        va_arg(*aq, u32);                                          \
+        break;                                                     \
+      case 8:                                                      \
+        va_arg(*aq, u64);                                          \
+        break;                                                     \
+      default:                                                     \
+        Report("WARNING: unexpected arg size"                      \
+               " in printf interceptor: %d\n", size);              \
+        return;                                                    \
+      }                                                            \
+    }                                                              \
+  } while (0)
+
+// Common part of *printf interceptors.
+// Process format string and va_list, and report all load ranges.
+static void printf_common(void *ctx, const char *format, va_list aq) {
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
+  const char *p = format;
+
+  while (*p) {
+    PrintfDirective dir;
+    p = printf_parse_next(p, &dir);
+    if (!p)
+      break;
+    if (dir.convSpecifier == 0) {
+      // This can only happen at the end of the format string.
+      CHECK_EQ(*p, 0);
+      break;
+    }
+    // Here the directive is valid. Do what it says.
+    if (dir.argIdx != -1 || dir.precisionIdx != -1) {
+      // Unsupported.
+      break;
+    }
+    if (dir.starredWidth) {
+      // Dynamic width
+      SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+    }
+    if (dir.starredPrecision) {
+      // Dynamic precision
+      SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+    }
+    int size = printf_get_value_size(&dir);
+    if (size == FSS_INVALID) {
+      Report("WARNING: unexpected format specifier in printf "
+             "interceptor: %.*s\n", dir.end - dir.begin, dir.begin);
+      break;
+    }
+    if (dir.convSpecifier == 'n') {
+      void *argp = va_arg(aq, void *);
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
+      continue;
+    } else if (size == FSS_STRLEN) {
+      if (void *argp = va_arg(aq, void *)) {
+        size = internal_strlen((const char *)argp) + 1;
+        COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
+      }
+    } else {
+      // Skip non-pointer args
+      SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size);
+    }
+  }
+}

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc Tue Jan 21 05:58:33 2014
@@ -32,6 +32,7 @@ void SetCommonFlagsDefaults(CommonFlags
   f->leak_check_at_exit = true;
   f->allocator_may_return_null = false;
   f->print_summary = true;
+  f->check_printf = false;
 }
 
 void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
@@ -48,6 +49,7 @@ void ParseCommonFlagsFromString(CommonFl
   ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit");
   ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null");
   ParseFlag(str, &f->print_summary, "print_summary");
+  ParseFlag(str, &f->check_printf, "check_printf");
 
   // Do a sanity check for certain flags.
   if (f->malloc_context_size < 1)

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h Tue Jan 21 05:58:33 2014
@@ -56,6 +56,8 @@ struct CommonFlags {
   bool allocator_may_return_null;
   // If false, disable printing error summaries in addition to error reports.
   bool print_summary;
+  // Check printf arguments.
+  bool check_printf;
 };
 
 inline CommonFlags *common_flags() {

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h Tue Jan 21 05:58:33 2014
@@ -75,6 +75,11 @@
 #define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
 #define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX
 
+#ifndef SANITIZER_INTERCEPT_PRINTF
+# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX
+#endif
+
 #define SANITIZER_INTERCEPT_FREXP 1
 #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS
 

Modified: compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc Tue Jan 21 05:58:33 2014
@@ -15,17 +15,51 @@
 #include "interception/interception.h"
 #include "sanitizer_test_utils.h"
 #include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_common.h"
 #include "gtest/gtest.h"
 
 using namespace __sanitizer;
 
+#define COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size)                    \
+  do {                                                                         \
+    ((std::vector<unsigned> *)ctx)->push_back(size);                           \
+    ptr = ptr;                                                                 \
+  } while (0)
+
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size)                          \
+  COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size)
+
 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size)                         \
-  ((std::vector<unsigned> *)ctx)->push_back(size)
+  COMMON_INTERCEPTOR_READ_WRITE_RANGE(ctx, ptr, size)
 
 #include "sanitizer_common/sanitizer_common_interceptors_scanf.inc"
 
-static const char scanf_buf[] = "Test string.";
-static size_t scanf_buf_size = sizeof(scanf_buf);
+static const unsigned I = sizeof(int);
+static const unsigned L = sizeof(long);
+static const unsigned LL = sizeof(long long);
+static const unsigned S = sizeof(short);
+static const unsigned C = sizeof(char);
+static const unsigned D = sizeof(double);
+static const unsigned LD = sizeof(long double);
+static const unsigned F = sizeof(float);
+static const unsigned P = sizeof(char *);
+
+static void verifyFormatResults(const char *format, unsigned n,
+                                const std::vector<unsigned> &computed_sizes,
+                                va_list expected_sizes) {
+  // "+ 1" because of format string
+  ASSERT_EQ(n + 1,
+            computed_sizes.size()) << "Unexpected number of format arguments: '"
+                                   << format << "'";
+  for (unsigned i = 0; i < n; ++i)
+    EXPECT_EQ(va_arg(expected_sizes, unsigned), computed_sizes[i + 1])
+        << "Unexpect write size for argument " << i << ", format string '"
+        << format << "'";
+}
+
+static const char test_buf[] = "Test string.";
+static const size_t test_buf_size = sizeof(test_buf);
+
 static const unsigned SCANF_ARGS_MAX = 16;
 
 static void testScanf3(void *ctx, int result, bool allowGnuMalloc,
@@ -42,15 +76,10 @@ static void testScanf2(const char *forma
   std::vector<unsigned> scanf_sizes;
   // 16 args should be enough.
   testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format,
-             scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf,
-             scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf,
-             scanf_buf, scanf_buf, scanf_buf, scanf_buf);
-  ASSERT_EQ(n, scanf_sizes.size()) << "Unexpected number of format arguments: '"
-                                   << format << "'";
-  for (unsigned i = 0; i < n; ++i)
-    EXPECT_EQ(va_arg(expected_sizes, unsigned), scanf_sizes[i])
-        << "Unexpect write size for argument " << i << ", format string '"
-        << format << "'";
+             test_buf, test_buf, test_buf, test_buf, test_buf, test_buf,
+             test_buf, test_buf, test_buf, test_buf, test_buf, test_buf,
+             test_buf, test_buf, test_buf, test_buf);
+  verifyFormatResults(format, n, scanf_sizes, expected_sizes);
 }
 
 static void testScanf(const char *format, unsigned n, ...) {
@@ -76,16 +105,6 @@ static void testScanfNoGnuMalloc(const c
 }
 
 TEST(SanitizerCommonInterceptors, Scanf) {
-  const unsigned I = sizeof(int);          // NOLINT
-  const unsigned L = sizeof(long);         // NOLINT
-  const unsigned LL = sizeof(long long);   // NOLINT
-  const unsigned S = sizeof(short);        // NOLINT
-  const unsigned C = sizeof(char);         // NOLINT
-  const unsigned D = sizeof(double);       // NOLINT
-  const unsigned LD = sizeof(long double); // NOLINT
-  const unsigned F = sizeof(float);        // NOLINT
-  const unsigned P = sizeof(char *);       // NOLINT
-
   testScanf("%d", 1, I);
   testScanf("%d%d%d", 3, I, I, I);
   testScanf("ab%u%dc", 2, I, I);
@@ -113,10 +132,10 @@ TEST(SanitizerCommonInterceptors, Scanf)
   testScanf("%*d", 0);
 
   testScanf("%4d%8f%c", 3, I, F, C);
-  testScanf("%s%d", 2, scanf_buf_size, I);
-  testScanf("%[abc]", 1, scanf_buf_size);
+  testScanf("%s%d", 2, test_buf_size, I);
+  testScanf("%[abc]", 1, test_buf_size);
   testScanf("%4[bcdef]", 1, 5);
-  testScanf("%[]]", 1, scanf_buf_size);
+  testScanf("%[]]", 1, test_buf_size);
   testScanf("%8[^]%d0-9-]%c", 2, 9, C);
 
   testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I);
@@ -172,7 +191,54 @@ TEST(SanitizerCommonInterceptors, Scanf)
   testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, I);
   testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I);
 
-  testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, scanf_buf_size);
-  testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, scanf_buf_size,
-                   scanf_buf_size);
+  testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, test_buf_size);
+  testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, test_buf_size,
+                   test_buf_size);
+}
+
+static void testPrintf3(void *ctx, const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  printf_common(ctx, format, ap);
+  va_end(ap);
+}
+
+static void testPrintf2(const char *format, unsigned n,
+                       va_list expected_sizes) {
+  std::vector<unsigned> printf_sizes;
+  // 16 args should be enough.
+  testPrintf3((void *)&printf_sizes, format,
+             test_buf, test_buf, test_buf, test_buf, test_buf, test_buf,
+             test_buf, test_buf, test_buf, test_buf, test_buf, test_buf,
+             test_buf, test_buf, test_buf, test_buf);
+  verifyFormatResults(format, n, printf_sizes, expected_sizes);
+}
+
+static void testPrintf(const char *format, unsigned n, ...) {
+  va_list ap;
+  va_start(ap, n);
+  testPrintf2(format, n, ap);
+  va_end(ap);
+}
+
+TEST(SanitizerCommonInterceptors, Printf) {
+  // Only test functionality which differs from scanf
+
+  // Indexed arguments
+  testPrintf("%5$d", 0);
+  testPrintf("%.*5$d", 0);
+
+  // errno
+  testPrintf("%0-m", 0);
+
+  // Dynamic width
+  testPrintf("%*n", 1, I);
+  testPrintf("%*.10n", 1, I);
+
+  // Precision
+  testPrintf("%10.10n", 1, I);
+
+  // Dynamic precision
+  testPrintf("%.*n", 1, I);
+  testPrintf("%10.*n", 1, I);
 }

Added: compiler-rt/trunk/lib/tsan/lit_tests/printf-1.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/lit_tests/printf-1.c?rev=199729&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/lit_tests/printf-1.c (added)
+++ compiler-rt/trunk/lib/tsan/lit_tests/printf-1.c Tue Jan 21 05:58:33 2014
@@ -0,0 +1,16 @@
+// RUN: %clang_tsan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s
+// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s
+// RUN: %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+int main() {
+  volatile char c = '0';
+  volatile int x = 12;
+  volatile float f = 1.239;
+  volatile char s[] = "34";
+  printf("%c %d %.3f %s\n", c, x, f, s);
+  return 0;
+  // Check that printf works fine under Tsan.
+  // CHECK: 0 12 1.239 34
+}

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_stat.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_stat.cc?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_stat.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_stat.cc Tue Jan 21 05:58:33 2014
@@ -283,6 +283,24 @@ void StatOutput(u64 *stat) {
   name[StatInt___isoc99_scanf]           = "  scanf                           ";
   name[StatInt___isoc99_sscanf]          = "  sscanf                          ";
   name[StatInt___isoc99_fscanf]          = "  fscanf                          ";
+  name[StatInt_vprintf]                  = "  vprintf                         ";
+  name[StatInt_vsprintf]                 = "  vsprintf                        ";
+  name[StatInt_vsnprintf]                = "  vsnprintf                       ";
+  name[StatInt_vasprintf]                = "  vasprintf                       ";
+  name[StatInt_vfprintf]                 = "  vfprintf                        ";
+  name[StatInt_printf]                   = "  printf                          ";
+  name[StatInt_sprintf]                  = "  sprintf                         ";
+  name[StatInt_snprintf]                 = "  snprintf                        ";
+  name[StatInt_asprintf]                 = "  asprintf                        ";
+  name[StatInt_fprintf]                  = "  fprintf                         ";
+  name[StatInt___isoc99_vprintf]         = "  vprintf                         ";
+  name[StatInt___isoc99_vsprintf]        = "  vsprintf                        ";
+  name[StatInt___isoc99_vsnprintf]       = "  vsnprintf                       ";
+  name[StatInt___isoc99_vfprintf]        = "  vfprintf                        ";
+  name[StatInt___isoc99_printf]          = "  printf                          ";
+  name[StatInt___isoc99_sprintf]         = "  sprintf                         ";
+  name[StatInt___isoc99_snprintf]        = "  snprintf                        ";
+  name[StatInt___isoc99_fprintf]         = "  fprintf                         ";
   name[StatInt_on_exit]                  = "  on_exit                         ";
   name[StatInt___cxa_atexit]             = "  __cxa_atexit                    ";
   name[StatInt_localtime]                = "  localtime                       ";

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_stat.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_stat.h?rev=199729&r1=199728&r2=199729&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_stat.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_stat.h Tue Jan 21 05:58:33 2014
@@ -278,6 +278,26 @@ enum StatType {
   StatInt___isoc99_scanf,
   StatInt___isoc99_sscanf,
   StatInt___isoc99_fscanf,
+  StatInt_vprintf,
+  StatInt_vsprintf,
+  StatInt_vsnprintf,
+  StatInt_vasprintf,
+  StatInt_vfprintf,
+  StatInt_printf,
+  StatInt_sprintf,
+  StatInt_snprintf,
+  StatInt_asprintf,
+  StatInt_fprintf,
+  StatInt___isoc99_vprintf,
+  StatInt___isoc99_vsprintf,
+  StatInt___isoc99_vsnprintf,
+  StatInt___isoc99_vasprintf,
+  StatInt___isoc99_vfprintf,
+  StatInt___isoc99_printf,
+  StatInt___isoc99_sprintf,
+  StatInt___isoc99_snprintf,
+  StatInt___isoc99_asprintf,
+  StatInt___isoc99_fprintf,
   StatInt_on_exit,
   StatInt___cxa_atexit,
   StatInt_localtime,





More information about the llvm-commits mailing list