diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -51,6 +52,8 @@ return true; } +static const char kAtosMachPortEnvVar[] = "__check_mach_ports_lookup"; + class AtosSymbolizerProcess : public SymbolizerProcess { public: explicit AtosSymbolizerProcess(const char *path) @@ -85,9 +88,40 @@ "only %llu are available.\n", num_entries, custom_env_.getNumEnvEntries()); } + + if (SANITIZER_IOSSIM) { + // `atos` in the simulator is restricted in its ability to retrieve the + // task port for the target process (us) so we need to do extra work + // to pass our task port to it. + mach_port_t ports[]{mach_task_self()}; + kern_return_t ret = + mach_ports_register(mach_task_self(), ports, /*count=*/1); + CHECK_EQ(ret, KERN_SUCCESS); + + // Add environment variable that signals to `atos` that it should look + // for our task port. We try to do this without heap allocations because + // the symbolizer might be spawned in a low memory situation. + bool success = + custom_env_.set(kAtosMachPortEnvVar, pid_str_, /*overwrite=*/true, + /*allow_heap_allocations=*/false); + if (!success) { + CHECK_EQ(custom_env_.getNumAvailableStaticEnvEntries(), 0); + // Insertion failed. This should only happen if we ran out of static + // entry slots. Try removing the most recently added entry and add + // again. + CHECK_EQ(custom_env_.getNumAvailableStaticEnvEntries(), 0); + custom_env_.removeEntryAt(custom_env_.getNumEnvEntries() - 1); + // Try again. + success = + custom_env_.set(kAtosMachPortEnvVar, pid_str_, /*overwrite=*/true, + /*allow_heap_allocations=*/false); + CHECK(success); + } + CHECK(!custom_env_.usingHeap()); + } + return SymbolizerProcess::StartSymbolizerSubprocess(); } - bool ReachedEndOfOutput(const char *buffer, uptr length) const override { return (length >= 1 && buffer[length - 1] == '\n'); } @@ -110,7 +144,9 @@ } char pid_str_[16]; - EnvArray<512, 1> custom_env_; + // Enough static storage for 511 environment variables and + // the addition of the kAtosMachPortEnvVar entry we might add. + EnvArray<512, sizeof(kAtosMachPortEnvVar) + sizeof(pid_str_) - 1> custom_env_; }; static bool ParseCommandOutput(const char *str, uptr addr, char **out_name, diff --git a/compiler-rt/test/tsan/Darwin/no_call_setenv_in_symbolize.cpp b/compiler-rt/test/tsan/Darwin/no_call_setenv_in_symbolize.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/tsan/Darwin/no_call_setenv_in_symbolize.cpp @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// `handle_sigbus=0` is required because when the rdar://problem/58789439 bug was +// present TSan's runtime could derefence bad memory leading to SIGBUS being raised. +// If the signal was caught TSan would deadlock because it would try to run the +// symbolizer again. +// RUN: %env_tsan_opts=handle_sigbus=0,symbolize=1 %run %t 2>&1 | FileCheck %s +// RUN: %env_tsan_opts=handle_sigbus=0,symbolize=1 _check_mach_ports_lookup=some_value %run %t 2>&1 | FileCheck %s +#include +#include +#include + +const char *kEnvName = "__UNLIKELY_ENV_VAR_NAME__"; + +int main() { + if (getenv(kEnvName)) { + fprintf(stderr, "Env var %s should not be set\n", kEnvName); + abort(); + } + + // This will set an environment variable that isn't already in + // the environment array. This will cause Darwin's Libc to + // malloc() a new array. + if (setenv(kEnvName, "some_value", /*overwrite=*/1)) { + fprintf(stderr, "Failed to set %s \n", kEnvName); + abort(); + } + + // rdar://problem/58789439 + // Now trigger symbolization. If symbolization tries to call + // to `setenv` that adds a new environment variable, then Darwin + // Libc will call `realloc()` and TSan's runtime will hit + // an assertion failure because TSan's runtime uses a different + // allocator during symbolization which leads to `realloc()` being + // called on a pointer that the allocator didn't allocate. + // + // CHECK: #{{[0-9]}} main {{.*}}no_call_setenv_in_symbolize.cpp:[[@LINE+1]] + __sanitizer_print_stack_trace(); + + // CHECK: DONE + fprintf(stderr, "DONE\n"); + + return 0; +} diff --git a/compiler-rt/test/tsan/Darwin/symbolizer-large-env.cpp b/compiler-rt/test/tsan/Darwin/symbolizer-large-env.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/tsan/Darwin/symbolizer-large-env.cpp @@ -0,0 +1,49 @@ +// FIXME(dliew): This duplicates a sanitizer_common test case because it doesn't run for iOSSim (rdar://problem/47333049). +// RUN: %clangxx_tsan %s -g -O0 -o %t +// RUN: %run %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +extern "C" char ***_NSGetEnviron(void); + +size_t get_num_env_entries() { + char **envp = *_NSGetEnviron(); + size_t count = 0; + for (; envp[count]; ++count) { + } + return count; +} + +const size_t kMaxNumEnvPEntries = 511; + +int main() { + size_t current_entries = get_num_env_entries(); + fprintf(stderr, "current_entries: %lu\n", current_entries); + size_t entries_to_add = (kMaxNumEnvPEntries - current_entries) + 1; + fprintf(stderr, "Adding %lu entries\n", entries_to_add); + + // Add a large number of entries. In particular more than the sanitizer + // run time allows for. + char buffer[256]; + for (int count = 0; count < entries_to_add; ++count) { + int chars_written = snprintf(buffer, sizeof(buffer), "__UNLIKELY_ENV_VAR__%d", count); + assert(chars_written); + if (setenv(buffer, "some_value", /*overwrite*/ 1)) { + fprintf(stderr, "Failed to write \"%s\" to env\n", buffer); + return 1; + } + } + current_entries = get_num_env_entries(); + assert(current_entries == kMaxNumEnvPEntries + 1); + + // Trigger symbolization. + // CHECK: WARNING: Truncating symbolizer environment. Needed 512 entries but only 511 are available. + // CHECK: #0{{( *0x.* *in *)?}} __sanitizer_print_stack_trace + // CHECK: #1{{( *0x.* *in *)?}} main {{.*}}symbolizer-large-env.cpp:[[@LINE+1]] + __sanitizer_print_stack_trace(); + + return 0; +}