summaryrefslogtreecommitdiff
path: root/lib/asan/asan_mac.cc
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2013-02-15 16:10:49 +0000
committerAlexander Potapenko <glider@google.com>2013-02-15 16:10:49 +0000
commitfe984cc9d8aa86cf6d5aa802b223a588d6f14105 (patch)
treee85deabd76385a34676f81fe49a5f31b467a061a /lib/asan/asan_mac.cc
parente31eca900a1f8849af75100c2d92e838d79d0920 (diff)
downloadcompiler-rt-fe984cc9d8aa86cf6d5aa802b223a588d6f14105.tar.gz
compiler-rt-fe984cc9d8aa86cf6d5aa802b223a588d6f14105.tar.bz2
compiler-rt-fe984cc9d8aa86cf6d5aa802b223a588d6f14105.tar.xz
[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. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@175276 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/asan/asan_mac.cc')
-rw-r--r--lib/asan/asan_mac.cc100
1 files changed, 91 insertions, 9 deletions
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index 264d8ed4..f6605b79 100644
--- a/lib/asan/asan_mac.cc
+++ b/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);
+ }
}
}