[compiler-rt] Make MaybeReexec properly process DYLD_INSERT_LIBRARIES when using...
authorKuba Brecka <kuba.brecka@gmail.com>
Fri, 6 Feb 2015 12:07:29 +0000 (12:07 +0000)
committerKuba Brecka <kuba.brecka@gmail.com>
Fri, 6 Feb 2015 12:07:29 +0000 (12:07 +0000)
MaybeReexec() in asan_mac.cc checks for presence of the ASan dylib in DYLD_INSERT_LIBRARIES, and if it is there, it will process this env. var. and remove the dylib from its value, so that spawned children don't have this variable set. However, the current implementation only works when using a canonical absolute path to the dylib, it fails to remove the dylib for example when using @executable_path.

This patch changes the processing of DYLD_INSERT_LIBRARIES to comparing values only based on filenames (ignoring directories).

Reviewed at http://reviews.llvm.org/D7160

llvm-svn: 228392

compiler-rt/lib/asan/asan_mac.cc
compiler-rt/lib/sanitizer_common/sanitizer_libc.cc
compiler-rt/lib/sanitizer_common/sanitizer_libc.h
compiler-rt/test/asan/TestCases/Darwin/dyld_insert_libraries_remove.cc [new file with mode: 0644]

index 0565412..5c2caea 100644 (file)
@@ -112,8 +112,10 @@ void MaybeReexec() {
   uptr old_env_len = dyld_insert_libraries ?
       internal_strlen(dyld_insert_libraries) : 0;
   uptr fname_len = internal_strlen(info.dli_fname);
+  const char *dylib_name = StripModuleName(info.dli_fname);
+  uptr dylib_name_len = internal_strlen(dylib_name);
   if (!dyld_insert_libraries ||
-      !REAL(strstr)(dyld_insert_libraries, StripModuleName(info.dli_fname))) {
+      !REAL(strstr)(dyld_insert_libraries, dylib_name)) {
     // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
     // library.
     char program_name[1024];
@@ -148,56 +150,65 @@ void MaybeReexec() {
            "possibly because of sandbox restrictions. Make sure to launch the "
            "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
     CHECK("execv failed" && 0);
-  } else {
-    // DYLD_INSERT_LIBRARIES is set and contains the runtime library.
-    if (old_env_len == fname_len) {
-      // It's just the runtime library name - fine to unset the variable.
-      LeakyResetEnv(kDyldInsertLibraries, NULL);
+  }
+
+  // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
+  // the dylib from the environment variable, because interceptors are installed
+  // and we don't want our children to inherit the variable.
+
+  uptr env_name_len = internal_strlen(kDyldInsertLibraries);
+  // Allocate memory to hold the previous env var name, its value, the '='
+  // sign and the '\0' char.
+  char *new_env = (char*)allocator_for_env.Allocate(
+      old_env_len + 2 + env_name_len);
+  CHECK(new_env);
+  internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
+  internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
+  new_env[env_name_len] = '=';
+  char *new_env_pos = new_env + env_name_len + 1;
+
+  // Iterate over colon-separated pieces of |dyld_insert_libraries|.
+  char *piece_start = dyld_insert_libraries;
+  char *piece_end = NULL;
+  char *old_env_end = dyld_insert_libraries + old_env_len;
+  do {
+    if (piece_start[0] == ':') piece_start++;
+    piece_end = REAL(strchr)(piece_start, ':');
+    if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
+    if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
+    uptr piece_len = piece_end - piece_start;
+
+    char *filename_start =
+        (char *)internal_memrchr(piece_start, '/', piece_len);
+    uptr filename_len = piece_len;
+    if (filename_start) {
+      filename_start += 1;
+      filename_len = piece_len - (filename_start - piece_start);
     } else {
-      uptr env_name_len = internal_strlen(kDyldInsertLibraries);
-      // Allocate memory to hold the previous env var name, its value, the '='
-      // sign and the '\0' char.
-      char *new_env = (char*)allocator_for_env.Allocate(
-          old_env_len + 2 + env_name_len);
-      CHECK(new_env);
-      internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
-      internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
-      new_env[env_name_len] = '=';
-      char *new_env_pos = new_env + env_name_len + 1;
-
-      // Iterate over colon-separated pieces of |dyld_insert_libraries|.
-      char *piece_start = dyld_insert_libraries;
-      char *piece_end = NULL;
-      char *old_env_end = dyld_insert_libraries + old_env_len;
-      do {
-        if (piece_start[0] == ':') piece_start++;
-        piece_end =  REAL(strchr)(piece_start, ':');
-        if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
-        if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
-        uptr piece_len = piece_end - piece_start;
-
-        // If the current piece isn't the runtime library name,
-        // append it to new_env.
-        if ((piece_len != fname_len) ||
-            (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) {
-          if (new_env_pos != new_env + env_name_len + 1) {
-            new_env_pos[0] = ':';
-            new_env_pos++;
-          }
-          internal_strncpy(new_env_pos, piece_start, piece_len);
-        }
-        // Move on to the next piece.
-        new_env_pos += piece_len;
-        piece_start = piece_end;
-      } while (piece_start < old_env_end);
-
-      // Can't use setenv() here, because it requires the allocator to be
-      // initialized.
-      // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
-      // a separate function called after InitializeAllocator().
-      LeakyResetEnv(kDyldInsertLibraries, new_env);
+      filename_start = piece_start;
     }
-  }
+
+    // If the current piece isn't the runtime library name,
+    // append it to new_env.
+    if ((dylib_name_len != filename_len) ||
+        (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
+      if (new_env_pos != new_env + env_name_len + 1) {
+        new_env_pos[0] = ':';
+        new_env_pos++;
+      }
+      internal_strncpy(new_env_pos, piece_start, piece_len);
+      new_env_pos += piece_len;
+    }
+    // Move on to the next piece.
+    piece_start = piece_end;
+  } while (piece_start < old_env_end);
+
+  // Can't use setenv() here, because it requires the allocator to be
+  // initialized.
+  // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
+  // a separate function called after InitializeAllocator().
+  if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
+  LeakyResetEnv(kDyldInsertLibraries, new_env);
 }
 
 // No-op. Mac does not support static linkage anyway.
index 862cf68..cb162a4 100644 (file)
@@ -28,6 +28,15 @@ void *internal_memchr(const void *s, int c, uptr n) {
   return 0;
 }
 
+void *internal_memrchr(const void *s, int c, uptr n) {
+  const char *t = (const char *)s;
+  void *res = nullptr;
+  for (uptr i = 0; i < n; ++i, ++t) {
+    if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
+  }
+  return res;
+}
+
 int internal_memcmp(const void* s1, const void* s2, uptr n) {
   const char *t1 = (const char *)s1;
   const char *t2 = (const char *)s2;
index 0f264f1..c086b8a 100644 (file)
@@ -26,6 +26,7 @@ namespace __sanitizer {
 // String functions
 s64 internal_atoll(const char *nptr);
 void *internal_memchr(const void *s, int c, uptr n);
+void *internal_memrchr(const void *s, int c, uptr n);
 int internal_memcmp(const void* s1, const void* s2, uptr n);
 void *internal_memcpy(void *dest, const void *src, uptr n);
 void *internal_memmove(void *dest, const void *src, uptr n);
diff --git a/compiler-rt/test/asan/TestCases/Darwin/dyld_insert_libraries_remove.cc b/compiler-rt/test/asan/TestCases/Darwin/dyld_insert_libraries_remove.cc
new file mode 100644 (file)
index 0000000..a3af8c1
--- /dev/null
@@ -0,0 +1,39 @@
+// Check that when launching with DYLD_INSERT_LIBRARIES, we properly remove
+// the ASan dylib from the environment variable (both when using an absolute
+// or relative path) and also that the other dylibs are left untouched.
+
+// RUN: mkdir -p %T/dyld_insert_libraries_remove
+// RUN: cp `%clang_asan %s -fsanitize=address -### 2>&1 \
+// RUN:   | grep "libclang_rt.asan_osx_dynamic.dylib" \
+// RUN:   | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \
+// RUN:   %T/dyld_insert_libraries_remove/libclang_rt.asan_osx_dynamic.dylib
+
+// RUN: %clangxx_asan %s -o %T/dyld_insert_libraries_remove/a.out
+// RUN: %clangxx -DSHARED_LIB %s \
+// RUN:     -dynamiclib -o %T/dyld_insert_libraries_remove/dummy-so.dylib
+
+// RUN: ( cd %T/dyld_insert_libraries_remove && \
+// RUN:   DYLD_INSERT_LIBRARIES=@executable_path/libclang_rt.asan_osx_dynamic.dylib:dummy-so.dylib \
+// RUN:   %run ./a.out 2>&1 ) | FileCheck %s || exit 1
+
+// RUN: ( cd %T/dyld_insert_libraries_remove && \
+// RUN:   DYLD_INSERT_LIBRARIES=libclang_rt.asan_osx_dynamic.dylib:dummy-so.dylib \
+// RUN:   %run ./a.out 2>&1 ) | FileCheck %s || exit 1
+
+// RUN: ( cd %T/dyld_insert_libraries_remove && \
+// RUN:   DYLD_INSERT_LIBRARIES=%T/dyld_insert_libraries_remove/libclang_rt.asan_osx_dynamic.dylib:dummy-so.dylib \
+// RUN:   %run ./a.out 2>&1 ) | FileCheck %s || exit 1
+
+#if !defined(SHARED_LIB)
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+  const char kEnvName[] = "DYLD_INSERT_LIBRARIES";
+  printf("%s=%s\n", kEnvName, getenv(kEnvName));
+  // CHECK: {{DYLD_INSERT_LIBRARIES=dummy-so.dylib}}
+  return 0;
+}
+#else  // SHARED_LIB
+void foo() {}
+#endif  // SHARED_LIB