[compiler-rt] r175276 - [ASan] Fix https://code.google.com/p/address-sanitizer/issues/detail?id=159

Alexander Potapenko glider at google.com
Fri Feb 15 08:10:49 PST 2013


Author: glider
Date: Fri Feb 15 10:10:49 2013
New Revision: 175276

URL: http://llvm.org/viewvc/llvm-project?rev=175276&view=rev
Log:
[ASan] Fix https://code.google.com/p/address-sanitizer/issues/detail?id=159
MaybeReexec() does now a tricky job to manage DYLD_INSERT_LIBRARIES in a safe way.

Because we're using library interposition, it's critical for an instrumented app
to be executed with the runtime library present in DYLD_INSERT_LIBRARIES list.
Therefore if it's initially missing in that list, we append the runtime library name
to the value of DYLD_INSERT_LIBRARIES and then exec() ourselves.

On the other hand, some of the apps exec()ed by our program may not want to have
ASan runtime library preloaded, so we remove the runtime library from the
DYLD_INSERT_LIBRARIES if it's already there.

Users may want to preload other libraries using DYLD_INSERT_LIBRARIES, so we preserve those.


Added:
    compiler-rt/trunk/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc
Modified:
    compiler-rt/trunk/lib/asan/asan_mac.cc

Modified: compiler-rt/trunk/lib/asan/asan_mac.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_mac.cc?rev=175276&r1=175275&r2=175276&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_mac.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_mac.cc Fri Feb 15 10:10:49 2013
@@ -88,6 +88,39 @@ extern "C"
 void __asan_init();
 
 static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+LowLevelAllocator allocator_for_env;
+
+// Change the value of the env var |name|, leaking the original value.
+// If |name_value| is NULL, the variable is deleted from the environment,
+// otherwise the corresponding "NAME=value" string is replaced with
+// |name_value|.
+void LeakyResetEnv(const char *name, const char *name_value) {
+  char ***env_ptr = _NSGetEnviron();
+  CHECK(env_ptr);
+  char **environ = *env_ptr;
+  CHECK(environ);
+  uptr name_len = internal_strlen(name);
+  while (*environ != 0) {
+    uptr len = internal_strlen(*environ);
+    if (len > name_len) {
+      const char *p = *environ;
+      if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
+        // Match.
+        if (name_value) {
+          // Replace the old value with the new one.
+          *environ = const_cast<char*>(name_value);
+        } else {
+          // Shift the subsequent pointers back.
+          char **del = environ;
+          do {
+            del[0] = del[1];
+          } while(*del++);
+        }
+      }
+    }
+    environ++;
+  }
+}
 
 void MaybeReexec() {
   if (!flags()->allow_reexec) return;
@@ -96,7 +129,11 @@ void MaybeReexec() {
   // ourselves.
   Dl_info info;
   CHECK(dladdr((void*)((uptr)__asan_init), &info));
-  const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
+  char *dyld_insert_libraries =
+      const_cast<char*>(GetEnv(kDyldInsertLibraries));
+  sptr old_env_len = dyld_insert_libraries ?
+      internal_strlen(dyld_insert_libraries) : 0;
+  sptr fname_len = internal_strlen(info.dli_fname);
   if (!dyld_insert_libraries ||
       !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
     // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
@@ -104,21 +141,18 @@ void MaybeReexec() {
     char program_name[1024];
     uint32_t buf_size = sizeof(program_name);
     _NSGetExecutablePath(program_name, &buf_size);
-    // Ok to use setenv() since the wrappers don't depend on the value of
-    // asan_inited.
+    char *new_env = const_cast<char*>(info.dli_fname);
     if (dyld_insert_libraries) {
       // Append the runtime dylib name to the existing value of
       // DYLD_INSERT_LIBRARIES.
-      uptr old_env_len = internal_strlen(dyld_insert_libraries);
-      uptr fname_len = internal_strlen(info.dli_fname);
-      LowLevelAllocator allocator_for_env;
-      char *new_env =
-          (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
+      new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
       internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
       new_env[old_env_len] = ':';
       // Copy fname_len and add a trailing zero.
       internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
                        fname_len + 1);
+      // Ok to use setenv() since the wrappers don't depend on the value of
+      // asan_inited.
       setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
     } else {
       // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
@@ -126,11 +160,59 @@ void MaybeReexec() {
     }
     if (flags()->verbosity >= 1) {
       Report("exec()-ing the program with\n");
-      Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname);
+      Report("%s=%s\n", kDyldInsertLibraries, new_env);
       Report("to enable ASan wrappers.\n");
       Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
     }
     execv(program_name, *_NSGetArgv());
+  } 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);
+    } else {
+      sptr 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 (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);
+    }
   }
 }
 

Added: compiler-rt/trunk/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc?rev=175276&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc Fri Feb 15 10:10:49 2013
@@ -0,0 +1,20 @@
+// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before
+// executing other programs.
+
+// RUN: %clangxx_asan -m64 %s -o %t
+// RUN: %clangxx -m64 %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \
+// RUN:     -dynamiclib -o darwin-dummy-shared-lib-so.dylib
+
+// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before
+// execl().
+
+// RUN: %t >/dev/null 2>&1
+// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \
+// RUN:     %t 2>&1 | FileCheck %s || exit 1
+#include <unistd.h>
+int main() {
+  execl("/bin/bash", "/bin/bash", "-c",
+        "echo DYLD_INSERT_LIBRARIES=$DYLD_INSERT_LIBRARIES", NULL);
+  // CHECK:  {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}}
+  return 0;
+}





More information about the llvm-commits mailing list