[sanitizer] Intercept realpath and canonicalize_file_name.

Handle realpath(path, NULL) form.


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@185921 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index b9b722e..a8e260b 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -99,6 +99,9 @@
 // ---------------------- Wrappers ---------------- {{{1
 using namespace __asan;  // NOLINT
 
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
+
 #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
   ASAN_WRITE_RANGE(ptr, size)
 #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size)
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
index aa3342c..13d67b7 100644
--- a/lib/msan/msan_interceptors.cc
+++ b/lib/msan/msan_interceptors.cc
@@ -619,14 +619,6 @@
   return res;
 }
 
-INTERCEPTOR(char *, realpath, char *path, char *abspath) {
-  ENSURE_MSAN_INITED();
-  char *res = REAL(realpath)(path, abspath);
-  if (res)
-    __msan_unpoison(abspath, REAL(strlen)(abspath) + 1);
-  return res;
-}
-
 INTERCEPTOR(int, getrlimit, int resource, void *rlim) {
   if (msan_init_is_running)
     return REAL(getrlimit)(resource, rlim);
@@ -1208,7 +1200,6 @@
   INTERCEPT_FUNCTION(socketpair);
   INTERCEPT_FUNCTION(fgets);
   INTERCEPT_FUNCTION(fgets_unlocked);
-  INTERCEPT_FUNCTION(realpath);
   INTERCEPT_FUNCTION(getrlimit);
   INTERCEPT_FUNCTION(getrlimit64);
   INTERCEPT_FUNCTION(statfs);
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index baf7956..b73e35c 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -939,6 +939,23 @@
   EXPECT_NOT_POISONED(path[0]);
 }
 
+TEST(MemorySanitizer, realpath_null) {
+  const char* relpath = ".";
+  char* res = realpath(relpath, NULL);
+  printf("%d, %s\n", errno, strerror(errno));
+  assert(res);
+  EXPECT_NOT_POISONED(res[0]);
+  free(res);
+}
+
+TEST(MemorySanitizer, canonicalize_file_name) {
+  const char* relpath = ".";
+  char* res = canonicalize_file_name(relpath);
+  assert(res);
+  EXPECT_NOT_POISONED(res[0]);
+  free(res);
+}
+
 TEST(MemorySanitizer, memcpy) {
   char* x = new char[2];
   char* y = new char[2];
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 5c7de75..702e6bb 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -1580,6 +1580,46 @@
 #define INIT_TCGETATTR
 #endif
 
+
+#if SANITIZER_INTERCEPT_REALPATH
+INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+
+  // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest
+  // version of a versioned symbol. For realpath(), this gives us something
+  // (called __old_realpath) that does not handle NULL in the second argument.
+  // Handle it as part of the interceptor.
+  char *allocated_path = 0;
+  if (!resolved_path)
+    allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1);
+
+  char *res = REAL(realpath)(path, resolved_path);
+  if (allocated_path && !res)
+    WRAP(free)(allocated_path);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_REALPATH INTERCEPT_FUNCTION(realpath);
+#else
+#define INIT_REALPATH
+#endif
+
+#if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME
+INTERCEPTOR(char *, canonicalize_file_name, const char *path) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  char *res = REAL(canonicalize_file_name)(path);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_CANONICALIZE_FILE_NAME INTERCEPT_FUNCTION(canonicalize_file_name);
+#else
+#define INIT_CANONICALIZE_FILE_NAME
+#endif
+
 #define SANITIZER_COMMON_INTERCEPTORS_INIT \
   INIT_STRCASECMP;                         \
   INIT_STRNCASECMP;                        \
@@ -1634,4 +1674,6 @@
   INIT_MBSNRTOWCS;                         \
   INIT_WCSTOMBS;                           \
   INIT_WCSNRTOMBS;                         \
-  INIT_TCGETATTR;
+  INIT_TCGETATTR;                          \
+  INIT_REALPATH;                           \
+  INIT_CANONICALIZE_FILE_NAME;
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 6edb250..4a84e05 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -106,5 +106,7 @@
 # define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
 # define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
 # define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX
+# define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
 
 #endif  // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index 45218fc..abb6611 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -22,6 +22,7 @@
 #include <arpa/inet.h>
 #include <dirent.h>
 #include <grp.h>
+#include <limits.h>
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <net/route.h>
@@ -194,6 +195,8 @@
 #endif
 #endif
 
+  unsigned path_max = PATH_MAX;
+
   // ioctl arguments
   unsigned struct_arpreq_sz = sizeof(struct arpreq);
   unsigned struct_ifreq_sz = sizeof(struct ifreq);
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index 91f2f11..7258526 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -195,6 +195,8 @@
   extern int glob_nomatch;
 #endif
 
+  extern unsigned path_max;
+
 #if SANITIZER_LINUX && !SANITIZER_ANDROID && \
       (defined(__i386) || defined (__x86_64))
   extern unsigned struct_user_regs_struct_sz;
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index 1ce60e7..3f153b6 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -349,6 +349,8 @@
   name[StatInt_wcsrtombs]                = "  wcsrtombs                       ";
   name[StatInt_wcsnrtombs]               = "  wcsnrtombs                      ";
   name[StatInt_tcgetattr]                = "  tcgetattr                       ";
+  name[StatInt_realpath]                 = "  realpath                        ";
+  name[StatInt_canonicalize_file_name]   = "  canonicalize_file_name          ";
 
   name[StatAnnotation]                   = "Dynamic annotations               ";
   name[StatAnnotateHappensBefore]        = "  HappensBefore                   ";
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
index e57ce8d..36b298e 100644
--- a/lib/tsan/rtl/tsan_stat.h
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -344,6 +344,8 @@
   StatInt_wcsrtombs,
   StatInt_wcsnrtombs,
   StatInt_tcgetattr,
+  StatInt_realpath,
+  StatInt_canonicalize_file_name,
 
   // Dynamic annotations.
   StatAnnotation,