diff options
-rw-r--r-- | compiler-rt/lib/asan/asan_mac.cc | 100 | ||||
-rw-r--r-- | compiler-rt/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc | 20 |
2 files changed, 111 insertions, 9 deletions
diff --git a/compiler-rt/lib/asan/asan_mac.cc b/compiler-rt/lib/asan/asan_mac.cc index 264d8ed4fa7..f6605b79cb8 100644 --- a/compiler-rt/lib/asan/asan_mac.cc +++ b/compiler-rt/lib/asan/asan_mac.cc @@ -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); + } } } diff --git a/compiler-rt/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc b/compiler-rt/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 00000000000..cf89949cf94 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/Darwin/unset-insert-libraries-on-exec.cc @@ -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; +} |