Skip to content

Commit 86531c4

Browse files
Merge pull request #1095 from danliew-apple/cherry-pick-58789439_part3
Cherry pick rdar://problem/58789439 part3
2 parents f1a1c4b + cf8a197 commit 86531c4

9 files changed

+109
-3
lines changed

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,10 @@ Symbolizer::SymbolizerScope::~SymbolizerScope() {
126126
sym_->end_hook_();
127127
}
128128

129+
void Symbolizer::LateInitializeTools() {
130+
for (auto &tool : tools_) {
131+
tool.LateInitialize();
132+
}
133+
}
134+
129135
} // namespace __sanitizer

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ class Symbolizer final {
209209
private:
210210
const Symbolizer *sym_;
211211
};
212+
213+
// Calls `LateInitialize()` on all items in `tools_`.
214+
void LateInitializeTools();
212215
};
213216

214217
#ifdef SANITIZER_WINDOWS

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class SymbolizerTool {
6969
virtual const char *Demangle(const char *name) {
7070
return nullptr;
7171
}
72+
73+
// Called during the LateInitialize phase of Sanitizer initialization.
74+
// Usually this is a safe place to call code that might need to use user
75+
// memory allocators.
76+
virtual void LateInitialize() {}
7277
};
7378

7479
// SymbolizerProcess encapsulates communication between the tool and

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <dlfcn.h>
2222
#include <errno.h>
23+
#include <mach/mach.h>
2324
#include <stdlib.h>
2425
#include <sys/wait.h>
2526
#include <unistd.h>
@@ -50,13 +51,29 @@ bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
5051
return true;
5152
}
5253

54+
#define K_ATOS_ENV_VAR "__check_mach_ports_lookup"
55+
5356
class AtosSymbolizerProcess : public SymbolizerProcess {
5457
public:
5558
explicit AtosSymbolizerProcess(const char *path)
5659
: SymbolizerProcess(path, /*use_posix_spawn*/ true) {
5760
pid_str_[0] = '\0';
5861
}
5962

63+
void LateInitialize() {
64+
if (SANITIZER_IOSSIM) {
65+
// `putenv()` may call malloc/realloc so it is only safe to do this
66+
// during LateInitialize() or later (i.e. we can't do this in the
67+
// constructor). We also can't do this in `StartSymbolizerSubprocess()`
68+
// because in TSan we switch allocators when we're symbolizing.
69+
// We use `putenv()` rather than `setenv()` so that we can later directly
70+
// write into the storage without LibC getting involved to change what the
71+
// variable is set to
72+
int result = putenv(mach_port_env_var_entry_);
73+
CHECK_EQ(result, 0);
74+
}
75+
}
76+
6077
private:
6178
bool StartSymbolizerSubprocess() override {
6279
// Configure sandbox before starting atos process.
@@ -65,6 +82,28 @@ class AtosSymbolizerProcess : public SymbolizerProcess {
6582
// the call to GetArgV.
6683
internal_snprintf(pid_str_, sizeof(pid_str_), "%d", internal_getpid());
6784

85+
if (SANITIZER_IOSSIM) {
86+
// `atos` in the simulator is restricted in its ability to retrieve the
87+
// task port for the target process (us) so we need to do extra work
88+
// to pass our task port to it.
89+
mach_port_t ports[]{mach_task_self()};
90+
kern_return_t ret =
91+
mach_ports_register(mach_task_self(), ports, /*count=*/1);
92+
CHECK_EQ(ret, KERN_SUCCESS);
93+
94+
// Set environment variable that signals to `atos` that it should look
95+
// for our task port. We can't call `setenv()` here because it might call
96+
// malloc/realloc. To avoid that we instead update the
97+
// `mach_port_env_var_entry_` variable with our current PID.
98+
uptr count = internal_snprintf(mach_port_env_var_entry_,
99+
sizeof(mach_port_env_var_entry_),
100+
K_ATOS_ENV_VAR "=%s", pid_str_);
101+
CHECK_GE(count, sizeof(K_ATOS_ENV_VAR) + internal_strlen(pid_str_));
102+
// Document our assumption but without calling `getenv()` in normal
103+
// builds.
104+
DCHECK_EQ(internal_strcmp(getenv(K_ATOS_ENV_VAR), pid_str_), 0);
105+
}
106+
68107
return SymbolizerProcess::StartSymbolizerSubprocess();
69108
}
70109

@@ -88,8 +127,13 @@ class AtosSymbolizerProcess : public SymbolizerProcess {
88127
}
89128

90129
char pid_str_[16];
130+
// Space for `\0` in `kAtosEnvVar_` is reused for `=`.
131+
char mach_port_env_var_entry_[sizeof(K_ATOS_ENV_VAR) + sizeof(pid_str_)] =
132+
K_ATOS_ENV_VAR "=0";
91133
};
92134

135+
#undef K_ATOS_ENV_VAR
136+
93137
static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
94138
char **out_module, char **out_file, uptr *line,
95139
uptr *start_address) {
@@ -191,6 +235,8 @@ bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
191235
return true;
192236
}
193237

238+
void AtosSymbolizer::LateInitialize() { process_->LateInitialize(); }
239+
194240
} // namespace __sanitizer
195241

196242
#endif // SANITIZER_MAC

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class AtosSymbolizer : public SymbolizerTool {
3535

3636
bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
3737
bool SymbolizeData(uptr addr, DataInfo *info) override;
38+
void LateInitialize() override;
3839

3940
private:
4041
AtosSymbolizerProcess *process_;

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ Symbolizer *Symbolizer::PlatformInit() {
9494
return new (symbolizer_allocator_) Symbolizer({});
9595
}
9696

97-
void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
97+
void Symbolizer::LateInitialize() {
98+
Symbolizer::GetOrInit()->LateInitializeTools();
99+
}
98100

99101
void StartReportDeadlySignal() {}
100102
void ReportDeadlySignal(const SignalContext &sig, u32 tid,

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ Symbolizer *Symbolizer::PlatformInit() {
488488
}
489489

490490
void Symbolizer::LateInitialize() {
491-
Symbolizer::GetOrInit();
491+
Symbolizer::GetOrInit()->LateInitializeTools();
492492
InitializeSwiftDemangler();
493493
}
494494

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ Symbolizer *Symbolizer::PlatformInit() {
310310
}
311311

312312
void Symbolizer::LateInitialize() {
313-
Symbolizer::GetOrInit();
313+
Symbolizer::GetOrInit()->LateInitializeTools();
314314
}
315315

316316
} // namespace __sanitizer
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: %clangxx_tsan -O1 %s -o %t
2+
// `handle_sigbus=0` is required because when the rdar://problem/58789439 bug was
3+
// present TSan's runtime could derefence bad memory leading to SIGBUS being raised.
4+
// If the signal was caught TSan would deadlock because it would try to run the
5+
// symbolizer again.
6+
// RUN: %env_tsan_opts=handle_sigbus=0,symbolize=1 %run %t 2>&1 | FileCheck %s
7+
// RUN: %env_tsan_opts=handle_sigbus=0,symbolize=1 __check_mach_ports_lookup=some_value %run %t 2>&1 | FileCheck %s
8+
#include <sanitizer/common_interface_defs.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
12+
const char *kEnvName = "__UNLIKELY_ENV_VAR_NAME__";
13+
14+
int main() {
15+
if (getenv(kEnvName)) {
16+
fprintf(stderr, "Env var %s should not be set\n", kEnvName);
17+
abort();
18+
}
19+
20+
// This will set an environment variable that isn't already in
21+
// the environment array. This will cause Darwin's Libc to
22+
// malloc() a new array.
23+
if (setenv(kEnvName, "some_value", /*overwrite=*/1)) {
24+
fprintf(stderr, "Failed to set %s \n", kEnvName);
25+
abort();
26+
}
27+
28+
// rdar://problem/58789439
29+
// Now trigger symbolization. If symbolization tries to call
30+
// to `setenv` that adds a new environment variable, then Darwin
31+
// Libc will call `realloc()` and TSan's runtime will hit
32+
// an assertion failure because TSan's runtime uses a different
33+
// allocator during symbolization which leads to `realloc()` being
34+
// called on a pointer that the allocator didn't allocate.
35+
//
36+
// CHECK: #{{[0-9]}} main {{.*}}no_call_setenv_in_symbolize.cpp:[[@LINE+1]]
37+
__sanitizer_print_stack_trace();
38+
39+
// CHECK: DONE
40+
fprintf(stderr, "DONE\n");
41+
42+
return 0;
43+
}

0 commit comments

Comments
 (0)