diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.h b/compiler-rt/lib/sanitizer_common/sanitizer_mac.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.h @@ -54,6 +54,9 @@ void RestrictMemoryToMaxAddress(uptr max_address); +// Get number of entries in environment array (excludes null terminator entry). +uptr GetNumEnvironEntries(char **envp); + } // namespace __sanitizer extern "C" { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp @@ -417,6 +417,13 @@ return environ; } +uptr GetNumEnvironEntries(char **envp) { + uptr count = 0; + for (; envp[count]; ++count) { + } + return count; +} + const char *GetEnv(const char *name) { char **env = GetEnviron(); uptr name_len = internal_strlen(name); 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,10 +14,6 @@ #include "sanitizer_platform.h" #if SANITIZER_MAC -#include "sanitizer_allocator_internal.h" -#include "sanitizer_mac.h" -#include "sanitizer_symbolizer_mac.h" - #include #include #include @@ -25,6 +21,11 @@ #include #include +#include "sanitizer_allocator_internal.h" +#include "sanitizer_env_array.h" +#include "sanitizer_mac.h" +#include "sanitizer_symbolizer_mac.h" + namespace __sanitizer { bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { @@ -65,6 +66,25 @@ // the call to GetArgV. internal_snprintf(pid_str_, sizeof(pid_str_), "%d", internal_getpid()); + // Prepare the environment. + custom_env_.reset(); + + // Copy the existing environment. + // FIXME(dliew): This isn't quite right. There's a race here because we + // shallow copy the strings which we don't own which could be free'd between + // now and the time we launch the process. Deep copying wouldn't help much + // because the exact same race exists. Really we need to intercept `setenv` + // and friends and take a lock until we finish launching the process. + uptr entries_copied = custom_env_.copyFromRawEnv( + GetEnviron(), /*shallow_copy=*/true, /*allow_heap_allocations=*/false, + /*overwrite=*/true); + uptr num_entries = GetNumEnvironEntries(GetEnviron()); + if (entries_copied < num_entries) { + Report( + "WARNING: Truncating symbolizer environment. Needed %llu entries but " + "only %llu are available.\n", + num_entries, custom_env_.getNumEnvEntries()); + } return SymbolizerProcess::StartSymbolizerSubprocess(); } @@ -72,6 +92,8 @@ return (length >= 1 && buffer[length - 1] == '\n'); } + char **GetEnvP() override { return custom_env_.getStorage().data(); } + void GetArgV(const char *path_to_binary, const char *(&argv)[kArgVMax]) const override { int i = 0; @@ -88,6 +110,7 @@ } char pid_str_[16]; + EnvArray<512, 1> custom_env_; }; static bool ParseCommandOutput(const char *str, uptr addr, char **out_name, diff --git a/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-large-env.cpp b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-large-env.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Darwin/symbolizer-large-env.cpp @@ -0,0 +1,48 @@ +// RUN: %clangxx %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; +}