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 <dlfcn.h>
 #include <errno.h>
+#include <mach/mach.h>
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <util.h>
 
+#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 <sanitizer/common_interface_defs.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}