[asan] Recommit of r301904: Add strndup/__strndup interceptors
authorPierre Gousseau <pierregousseau14@gmail.com>
Thu, 11 May 2017 08:53:24 +0000 (08:53 +0000)
committerPierre Gousseau <pierregousseau14@gmail.com>
Thu, 11 May 2017 08:53:24 +0000 (08:53 +0000)
Fix undeclared __interceptor_malloc in esan_interceptors.cc
Fix undeclared strnlen on OSX

Differential Revision: https://reviews.llvm.org/D31457

llvm-svn: 302781

compiler-rt/lib/asan/asan_flags.cc
compiler-rt/lib/asan/tests/asan_str_test.cc
compiler-rt/lib/esan/esan_interceptors.cpp
compiler-rt/lib/msan/msan_interceptors.cc
compiler-rt/lib/msan/tests/msan_test.cc
compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h
compiler-rt/test/asan/TestCases/Posix/strndup_oob_test.cc [new file with mode: 0644]
compiler-rt/test/msan/strndup.cc [new file with mode: 0644]

index c8ae3fa..6be0d6e 100644 (file)
@@ -194,6 +194,10 @@ void InitializeFlags() {
     Report("WARNING: strchr* interceptors are enabled even though "
            "replace_str=0. Use intercept_strchr=0 to disable them.");
   }
+  if (!f->replace_str && common_flags()->intercept_strndup) {
+    Report("WARNING: strndup* interceptors are enabled even though "
+           "replace_str=0. Use intercept_strndup=0 to disable them.");
+  }
 }
 
 }  // namespace __asan
index c790088..8f4911f 100644 (file)
@@ -154,6 +154,27 @@ TEST(AddressSanitizer, MAYBE_StrDupOOBTest) {
   free(str);
 }
 
+#if SANITIZER_TEST_HAS_STRNDUP
+TEST(AddressSanitizer, MAYBE_StrNDupOOBTest) {
+  size_t size = Ident(42);
+  char *str = MallocAndMemsetString(size);
+  char *new_str;
+  // Normal strndup calls.
+  str[size - 1] = '\0';
+  new_str = strndup(str, size - 13);
+  free(new_str);
+  new_str = strndup(str + size - 1, 13);
+  free(new_str);
+  // Argument points to not allocated memory.
+  EXPECT_DEATH(Ident(strndup(str - 1, 13)), LeftOOBReadMessage(1));
+  EXPECT_DEATH(Ident(strndup(str + size, 13)), RightOOBReadMessage(0));
+  // Overwrite the terminating '\0' and hit unallocated memory.
+  str[size - 1] = 'z';
+  EXPECT_DEATH(Ident(strndup(str, size + 13)), RightOOBReadMessage(0));
+  free(str);
+}
+#endif // SANITIZER_TEST_HAS_STRNDUP
+
 TEST(AddressSanitizer, StrCpyOOBTest) {
   size_t to_size = Ident(30);
   size_t from_size = Ident(6);  // less than to_size
index 9740f4d..62fa13c 100644 (file)
@@ -31,6 +31,8 @@ using namespace __esan; // NOLINT
 // Get the per-platform defines for what is possible to intercept
 #include "sanitizer_common/sanitizer_platform_interceptors.h"
 
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
+
 // TODO(bruening): tsan disables several interceptors (getpwent, etc.) claiming
 // that interception is a perf hit: should we do the same?
 
index 15543bd..0f50693 100644 (file)
@@ -341,33 +341,6 @@ INTERCEPTOR(char *, __strdup, char *src) {
 #define MSAN_MAYBE_INTERCEPT___STRDUP
 #endif
 
-INTERCEPTOR(char *, strndup, char *src, SIZE_T n) {
-  ENSURE_MSAN_INITED();
-  GET_STORE_STACK_TRACE;
-  // On FreeBSD strndup() leverages strnlen().
-  InterceptorScope interceptor_scope;
-  SIZE_T copy_size = REAL(strnlen)(src, n);
-  char *res = REAL(strndup)(src, n);
-  CopyShadowAndOrigin(res, src, copy_size, &stack);
-  __msan_unpoison(res + copy_size, 1); // \0
-  return res;
-}
-
-#if !SANITIZER_FREEBSD
-INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) {
-  ENSURE_MSAN_INITED();
-  GET_STORE_STACK_TRACE;
-  SIZE_T copy_size = REAL(strnlen)(src, n);
-  char *res = REAL(__strndup)(src, n);
-  CopyShadowAndOrigin(res, src, copy_size, &stack);
-  __msan_unpoison(res + copy_size, 1); // \0
-  return res;
-}
-#define MSAN_MAYBE_INTERCEPT___STRNDUP INTERCEPT_FUNCTION(__strndup)
-#else
-#define MSAN_MAYBE_INTERCEPT___STRNDUP
-#endif
-
 INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) {
   ENSURE_MSAN_INITED();
   char *res = REAL(gcvt)(number, ndigit, buf);
@@ -1371,6 +1344,13 @@ int OnExit() {
     return __msan_memcpy(to, from, size);                   \
   }
 
+#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size)                    \
+  do {                                                                         \
+    GET_STORE_STACK_TRACE;                                                     \
+    CopyShadowAndOrigin(to, from, size, &stack);                               \
+    __msan_unpoison(to + size, 1);                                             \
+  } while (false)
+
 #include "sanitizer_common/sanitizer_platform_interceptors.h"
 #include "sanitizer_common/sanitizer_common_interceptors.inc"
 
@@ -1538,8 +1518,6 @@ void InitializeInterceptors() {
   INTERCEPT_FUNCTION(stpcpy);  // NOLINT
   INTERCEPT_FUNCTION(strdup);
   MSAN_MAYBE_INTERCEPT___STRDUP;
-  INTERCEPT_FUNCTION(strndup);
-  MSAN_MAYBE_INTERCEPT___STRNDUP;
   INTERCEPT_FUNCTION(strncpy);  // NOLINT
   INTERCEPT_FUNCTION(gcvt);
   INTERCEPT_FUNCTION(strcat);  // NOLINT
index 6cae218..40f5627 100644 (file)
@@ -1581,7 +1581,8 @@ TEST(MemorySanitizer, strdup) {
 TEST(MemorySanitizer, strndup) {
   char buf[4] = "abc";
   __msan_poison(buf + 2, sizeof(*buf));
-  char *x = strndup(buf, 3);
+  char *x;
+  EXPECT_UMR(x = strndup(buf, 3));
   EXPECT_NOT_POISONED(x[0]);
   EXPECT_NOT_POISONED(x[1]);
   EXPECT_POISONED(x[2]);
@@ -1593,7 +1594,8 @@ TEST(MemorySanitizer, strndup_short) {
   char buf[4] = "abc";
   __msan_poison(buf + 1, sizeof(*buf));
   __msan_poison(buf + 2, sizeof(*buf));
-  char *x = strndup(buf, 2);
+  char *x;
+  EXPECT_UMR(x = strndup(buf, 2));
   EXPECT_NOT_POISONED(x[0]);
   EXPECT_POISONED(x[1]);
   EXPECT_NOT_POISONED(x[2]);
index 53204b4..3c69726 100644 (file)
@@ -34,6 +34,8 @@
 //   COMMON_INTERCEPTOR_MEMSET_IMPL
 //   COMMON_INTERCEPTOR_MEMMOVE_IMPL
 //   COMMON_INTERCEPTOR_MEMCPY_IMPL
+//   COMMON_INTERCEPTOR_COPY_STRING
+//   COMMON_INTERCEPTOR_STRNDUP_IMPL
 //===----------------------------------------------------------------------===//
 
 #include "interception/interception.h"
@@ -217,6 +219,25 @@ bool PlatformHasDifferentMemcpyAndMemmove();
   }
 #endif
 
+#ifndef COMMON_INTERCEPTOR_COPY_STRING
+#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_STRNDUP_IMPL
+#define COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size)                          \
+  COMMON_INTERCEPTOR_ENTER(ctx, strndup, s, size);                             \
+  uptr from_length = internal_strnlen(s, size);                                \
+  uptr copy_length = Min(size, from_length);                                   \
+  char *new_mem = (char *)WRAP(malloc)(copy_length + 1);                       \
+  if (common_flags()->intercept_strndup) {                                     \
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s, copy_length + 1);                    \
+  }                                                                            \
+  COMMON_INTERCEPTOR_COPY_STRING(ctx, new_mem, s, copy_length);                \
+  internal_memcpy(new_mem, s, copy_length);                                    \
+  new_mem[copy_length] = '\0';                                                 \
+  return new_mem;
+#endif
+
 struct FileMetadata {
   // For open_memstream().
   char **addr;
@@ -300,6 +321,26 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T maxlen) {
 #define INIT_STRNLEN
 #endif
 
+#if SANITIZER_INTERCEPT_STRNDUP
+INTERCEPTOR(char*, strndup, const char *s, uptr size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
+}
+#define INIT_STRNDUP COMMON_INTERCEPT_FUNCTION(strndup)
+#else
+#define INIT_STRNDUP
+#endif // SANITIZER_INTERCEPT_STRNDUP
+
+#if SANITIZER_INTERCEPT___STRNDUP
+INTERCEPTOR(char*, __strndup, const char *s, uptr size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
+}
+#define INIT___STRNDUP COMMON_INTERCEPT_FUNCTION(__strndup)
+#else
+#define INIT___STRNDUP
+#endif // SANITIZER_INTERCEPT___STRNDUP
+
 #if SANITIZER_INTERCEPT_TEXTDOMAIN
 INTERCEPTOR(char*, textdomain, const char *domainname) {
   void *ctx;
@@ -6163,6 +6204,8 @@ static void InitializeCommonInterceptors() {
   INIT_TEXTDOMAIN;
   INIT_STRLEN;
   INIT_STRNLEN;
+  INIT_STRNDUP;
+  INIT___STRNDUP;
   INIT_STRCMP;
   INIT_STRNCMP;
   INIT_STRCASECMP;
index 7a5fffc..67a0a58 100644 (file)
@@ -195,6 +195,9 @@ COMMON_FLAG(bool, intercept_strpbrk, true,
 COMMON_FLAG(bool, intercept_strlen, true,
             "If set, uses custom wrappers for strlen and strnlen functions "
             "to find more errors.")
+COMMON_FLAG(bool, intercept_strndup, true,
+            "If set, uses custom wrappers for strndup functions "
+            "to find more errors.")
 COMMON_FLAG(bool, intercept_strchr, true,
             "If set, uses custom wrappers for strchr, strchrnul, and strrchr "
             "functions to find more errors.")
index e5644ef..a954974 100644 (file)
 # define SI_NOT_WINDOWS 0
 #endif
 
+#if SANITIZER_POSIX
+# define SI_POSIX 1
+#else
+# define SI_POSIX 0
+#endif
+
 #if SANITIZER_LINUX && !SANITIZER_ANDROID
 # define SI_LINUX_NOT_ANDROID 1
 #else
 # define SI_UNIX_NOT_MAC 0
 #endif
 
+#if SANITIZER_LINUX && !SANITIZER_FREEBSD
+# define SI_LINUX_NOT_FREEBSD 1
+# else
+# define SI_LINUX_NOT_FREEBSD 0
+#endif
+
 #define SANITIZER_INTERCEPT_STRLEN 1
 #define SANITIZER_INTERCEPT_STRNLEN SI_NOT_MAC
 #define SANITIZER_INTERCEPT_STRCMP 1
@@ -86,6 +98,8 @@
 #define SANITIZER_INTERCEPT_MEMMOVE 1
 #define SANITIZER_INTERCEPT_MEMCPY 1
 #define SANITIZER_INTERCEPT_MEMCMP 1
+#define SANITIZER_INTERCEPT_STRNDUP SI_POSIX
+#define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD
 #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
 # define SI_MAC_DEPLOYMENT_BELOW_10_7 1
index 9c162a6..b7728d9 100644 (file)
@@ -124,4 +124,10 @@ static inline uint32_t my_rand() {
 # define SANITIZER_TEST_HAS_PRINTF_L 0
 #endif
 
+#if !defined(_MSC_VER)
+# define SANITIZER_TEST_HAS_STRNDUP 1
+#else
+# define SANITIZER_TEST_HAS_STRNDUP 0
+#endif
+
 #endif  // SANITIZER_TEST_UTILS_H
diff --git a/compiler-rt/test/asan/TestCases/Posix/strndup_oob_test.cc b/compiler-rt/test/asan/TestCases/Posix/strndup_oob_test.cc
new file mode 100644 (file)
index 0000000..4367e24
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// When built as C on Linux, strndup is transformed to __strndup.
+// RUN: %clangxx_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: win32
+
+#include <string.h>
+
+char kString[] = "foo";
+
+int main(int argc, char **argv) {
+  char *copy = strndup(kString, 2);
+  int x = copy[2 + argc];  // BOOM
+  // CHECK: AddressSanitizer: heap-buffer-overflow
+  // CHECK: #0 {{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-2]]
+  // CHECK-LABEL: allocated by thread T{{.*}} here:
+  // CHECK: #{{[01]}} {{.*}}strndup
+  // CHECK: #{{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-6]]
+  // CHECK-LABEL: SUMMARY
+  // CHECK: strndup_oob_test.cc:[[@LINE-7]]
+  return x;
+}
diff --git a/compiler-rt/test/msan/strndup.cc b/compiler-rt/test/msan/strndup.cc
new file mode 100644 (file)
index 0000000..d4b9af1
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %clangxx_msan %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s
+// RUN: %clangxx_msan %s -o %t && MSAN_OPTIONS=intercept_strndup=0 %run %t 2>&1 | FileCheck --check-prefix=OFF --allow-empty %s
+
+// When built as C on Linux, strndup is transformed to __strndup.
+// RUN: %clangxx_msan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s
+
+// UNSUPPORTED: win32
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sanitizer/msan_interface.h>
+
+int main(int argc, char **argv) {
+  char kString[4] = "abc";
+  __msan_poison(kString + 2, 1);
+  char *copy = strndup(kString, 4); // BOOM
+  assert(__msan_test_shadow(copy, 4) == 2); // Poisoning is preserved.
+  free(copy);
+  return 0;
+  // ON: Uninitialized bytes in __interceptor_{{(__)?}}strndup at offset 2 inside [{{.*}}, 4)
+  // ON: MemorySanitizer: use-of-uninitialized-value
+  // ON: #0 {{.*}}main {{.*}}strndup.cc:[[@LINE-6]]
+  // ON-LABEL: SUMMARY
+  // ON: {{.*}}strndup.cc:[[@LINE-8]]
+  // OFF-NOT: MemorySanitizer
+}
+