diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h @@ -35,6 +35,7 @@ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; bool SymbolizeData(uptr addr, DataInfo *info) override; + void LateInitialize() override; private: AtosSymbolizerProcess *process_; 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 @@ -14,17 +14,18 @@ #include "sanitizer_platform.h" #if SANITIZER_MAC -#include "sanitizer_allocator_internal.h" -#include "sanitizer_mac.h" -#include "sanitizer_symbolizer_mac.h" - #include #include +#include #include #include #include #include +#include "sanitizer_allocator_internal.h" +#include "sanitizer_mac.h" +#include "sanitizer_symbolizer_mac.h" + namespace __sanitizer { bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { @@ -49,6 +50,7 @@ datainfo->start = (uptr)info.dli_saddr; return true; } +static const char kAtosEnvVarEntryPrefix[] = "__check_mach_ports_lookup="; class AtosSymbolizerProcess : public SymbolizerProcess { public: @@ -57,6 +59,17 @@ pid_str_[0] = '\0'; } + void LateInitialize() { + if (SANITIZER_IOSSIM) { + // `putenv()` may call malloc/realloc so it is only safe to do this during + // LateInitialize() or later (i.e. we can't do this in the constructor). + // We use `putenv()` rather than `setenv()` so that we can later directly + // write into the storage without LibC getting involved to change what the + // variable is set to + CHECK_EQ(putenv(atosMachPortEnvVarEntry_), 0); + } + } + private: bool StartSymbolizerSubprocess() override { // Configure sandbox before starting atos process. @@ -65,6 +78,25 @@ // the call to GetArgV. internal_snprintf(pid_str_, sizeof(pid_str_), "%d", internal_getpid()); + 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); + + // Set environment variable that signals to `atos` that it should look + // for our task port. We can't call `setenv()` here because it might call + // malloc/realloc. To avoid that we instead update the + // `atosMachPortEnvVarEntry_` variable with our current PID. + uptr count = internal_snprintf(atosMachPortEnvVarEntry_, + sizeof(atosMachPortEnvVarEntry_), "%s%s", + kAtosEnvVarEntryPrefix, pid_str_); + CHECK_GE(count, sizeof(kAtosEnvVarEntryPrefix) + 1); + } + return SymbolizerProcess::StartSymbolizerSubprocess(); } @@ -88,6 +120,9 @@ } char pid_str_[16]; + char atosMachPortEnvVarEntry_[sizeof(kAtosEnvVarEntryPrefix) + + sizeof(pid_str_)] = + "__check_mach_ports_lookup=XXXXXXXXXXXXXXX"; }; static bool ParseCommandOutput(const char *str, uptr addr, char **out_name, @@ -191,6 +226,8 @@ return true; } +void AtosSymbolizer::LateInitialize() { process_->LateInitialize(); } + } // namespace __sanitizer #endif // SANITIZER_MAC 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; +}