Skip to content

Commit c8dd365

Browse files
pudge62maruipu
authored andcommitted
[tsan] Fix deadlock with dyld during symbolization on darwin platforms
On darwin platforms, callbacks registered via `_dyld_register_func_for_add_image` are invoked with a lock held by dyld. These callbacks, in turn, request locks in the TSan runtime due to instrumented codes or interceptors. Previously, reporting race issues involved holding TSan runtime locks and simultaneously attemping to acquire the dyld lock during symbolization, which leads to deadlock. This commit restructures the reporting process into 3 distinct steps: data collection, symbolization and printing. Each step now only holds the necessary locks to prevent the deadlock.
1 parent 7aabdb8 commit c8dd365

13 files changed

+254
-111
lines changed

compiler-rt/lib/tsan/go/tsan_go.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ ReportLocation *SymbolizeData(uptr addr) {
118118
}
119119
}
120120

121+
bool SymbolizeData(uptr addr, DataInfo *info) {
122+
SymbolizeDataContext cbctx;
123+
internal_memset(&cbctx, 0, sizeof(cbctx));
124+
cbctx.addr = addr;
125+
go_runtime_cb(CallbackSymbolizeData, &cbctx);
126+
if (!cbctx.res)
127+
return false;
128+
if (cbctx.heap) {
129+
return false;
130+
} else {
131+
info->Clear();
132+
info->name = internal_strdup(cbctx.name ? cbctx.name : "??");
133+
info->file = internal_strdup(cbctx.file ? cbctx.file : "??");
134+
info->line = cbctx.line;
135+
info->start = cbctx.start;
136+
info->size = cbctx.size;
137+
return true;
138+
}
139+
}
140+
121141
static ThreadState *main_thr;
122142
static bool inited;
123143

compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,13 +2064,17 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
20642064
// StackTrace::GetNestInstructionPc(pc) is used because return address is
20652065
// expected, OutputReport() will undo this.
20662066
ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
2067-
ThreadRegistryLock l(&ctx->thread_registry);
2068-
ScopedReport rep(ReportTypeErrnoInSignal);
2069-
rep.SetSigNum(sig);
2070-
if (!IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) {
2067+
if (IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) {
2068+
return;
2069+
}
2070+
ScopedReport rep(ReportTypeErrnoInSignal);
2071+
{
2072+
// ThreadRegistryLock l(&ctx->thread_registry);
2073+
rep.SetSigNum(sig);
20712074
rep.AddStack(stack, true);
2072-
OutputReport(thr, rep);
20732075
}
2076+
rep.SymbolizeReport();
2077+
OutputReport(thr, rep);
20742078
}
20752079

20762080
static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,

compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -437,15 +437,18 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
437437
}
438438

439439
static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
440-
ThreadRegistryLock l(&ctx->thread_registry);
441440
ScopedReport rep(ReportTypeMutexHeldWrongContext);
442-
for (uptr i = 0; i < thr->mset.Size(); ++i) {
443-
MutexSet::Desc desc = thr->mset.Get(i);
444-
rep.AddMutex(desc.addr, desc.stack_id);
441+
{
442+
ThreadRegistryLock l(&ctx->thread_registry);
443+
for (uptr i = 0; i < thr->mset.Size(); ++i) {
444+
MutexSet::Desc desc = thr->mset.Get(i);
445+
rep.AddMutex(desc.addr, desc.stack_id);
446+
}
447+
VarSizeStackTrace trace;
448+
ObtainCurrentStack(thr, pc, &trace);
449+
rep.AddStack(trace, true);
445450
}
446-
VarSizeStackTrace trace;
447-
ObtainCurrentStack(thr, pc, &trace);
448-
rep.AddStack(trace, true);
451+
rep.SymbolizeReport();
449452
OutputReport(thr, rep);
450453
}
451454

compiler-rt/lib/tsan/rtl/tsan_mman.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,12 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
182182
ObtainCurrentStack(thr, pc, &stack);
183183
if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
184184
return;
185-
ThreadRegistryLock l(&ctx->thread_registry);
186185
ScopedReport rep(ReportTypeSignalUnsafe);
187-
rep.AddStack(stack, true);
186+
{
187+
// ThreadRegistryLock l(&ctx->thread_registry);
188+
rep.AddStack(stack, true);
189+
}
190+
rep.SymbolizeReport();
188191
OutputReport(thr, rep);
189192
}
190193

compiler-rt/lib/tsan/rtl/tsan_report.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "sanitizer_common/sanitizer_thread_registry.h"
1717
#include "sanitizer_common/sanitizer_vector.h"
1818
#include "tsan_defs.h"
19+
#include "tsan_stack_trace.h"
1920

2021
namespace __tsan {
2122

@@ -39,6 +40,7 @@ enum ReportType {
3940
};
4041

4142
struct ReportStack {
43+
VarSizeStackTrace raw;
4244
SymbolizedStack *frames = nullptr;
4345
bool suppressable = false;
4446
};

compiler-rt/lib/tsan/rtl/tsan_rtl.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ uptr TagFromShadowStackFrame(uptr pc);
406406

407407
class ScopedReportBase {
408408
public:
409+
void SetKindAndTag(ReportType typ, uptr tag);
409410
void AddMemoryAccess(uptr addr, uptr external_tag, Shadow s, Tid tid,
410411
StackTrace stack, const MutexSet *mset);
411412
void AddStack(StackTrace stack, bool suppressable = false);
@@ -418,29 +419,26 @@ class ScopedReportBase {
418419
void SetCount(int count);
419420
void SetSigNum(int sig);
420421

422+
void SymbolizeReport();
421423
const ReportDesc *GetReport() const;
422424

423425
protected:
426+
ScopedReportBase();
424427
ScopedReportBase(ReportType typ, uptr tag);
425428
~ScopedReportBase();
426429

427430
private:
428431
ReportDesc *rep_;
429-
// Symbolizer makes lots of intercepted calls. If we try to process them,
430-
// at best it will cause deadlocks on internal mutexes.
431-
ScopedIgnoreInterceptors ignore_interceptors_;
432432

433433
ScopedReportBase(const ScopedReportBase &) = delete;
434434
void operator=(const ScopedReportBase &) = delete;
435435
};
436436

437437
class ScopedReport : public ScopedReportBase {
438438
public:
439+
explicit ScopedReport();
439440
explicit ScopedReport(ReportType typ, uptr tag = kExternalTagNone);
440441
~ScopedReport();
441-
442-
private:
443-
ScopedErrorReportLock lock_;
444442
};
445443

446444
bool ShouldReport(ThreadState *thr, ReportType typ);

compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
5555
return;
5656
if (!ShouldReport(thr, typ))
5757
return;
58-
ThreadRegistryLock l(&ctx->thread_registry);
5958
ScopedReport rep(typ);
60-
rep.AddMutex(addr, creation_stack_id);
61-
VarSizeStackTrace trace;
62-
ObtainCurrentStack(thr, pc, &trace);
63-
rep.AddStack(trace, true);
64-
rep.AddLocation(addr, 1);
59+
{
60+
ThreadRegistryLock l(&ctx->thread_registry);
61+
rep.AddMutex(addr, creation_stack_id);
62+
VarSizeStackTrace trace;
63+
ObtainCurrentStack(thr, pc, &trace);
64+
rep.AddStack(trace, true);
65+
rep.AddLocation(addr, 1);
66+
}
67+
rep.SymbolizeReport();
6568
OutputReport(thr, rep);
6669
}
6770

@@ -528,50 +531,56 @@ void AfterSleep(ThreadState *thr, uptr pc) {
528531
void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
529532
if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock))
530533
return;
531-
ThreadRegistryLock l(&ctx->thread_registry);
532534
ScopedReport rep(ReportTypeDeadlock);
533-
for (int i = 0; i < r->n; i++) {
534-
rep.AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
535-
rep.AddUniqueTid((int)r->loop[i].thr_ctx);
536-
rep.AddThread((int)r->loop[i].thr_ctx);
537-
}
538-
uptr dummy_pc = 0x42;
539-
for (int i = 0; i < r->n; i++) {
540-
for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
541-
u32 stk = r->loop[i].stk[j];
542-
if (stk && stk != kInvalidStackID) {
543-
rep.AddStack(StackDepotGet(stk), true);
544-
} else {
545-
// Sometimes we fail to extract the stack trace (FIXME: investigate),
546-
// but we should still produce some stack trace in the report.
547-
rep.AddStack(StackTrace(&dummy_pc, 1), true);
535+
{
536+
ThreadRegistryLock l(&ctx->thread_registry);
537+
for (int i = 0; i < r->n; i++) {
538+
rep.AddMutex(r->loop[i].mtx_ctx0, r->loop[i].stk[0]);
539+
rep.AddUniqueTid((int)r->loop[i].thr_ctx);
540+
rep.AddThread((int)r->loop[i].thr_ctx);
541+
}
542+
uptr dummy_pc = 0x42;
543+
for (int i = 0; i < r->n; i++) {
544+
for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
545+
u32 stk = r->loop[i].stk[j];
546+
if (stk && stk != kInvalidStackID) {
547+
rep.AddStack(StackDepotGet(stk), true);
548+
} else {
549+
// Sometimes we fail to extract the stack trace (FIXME: investigate),
550+
// but we should still produce some stack trace in the report.
551+
rep.AddStack(StackTrace(&dummy_pc, 1), true);
552+
}
548553
}
549554
}
550555
}
556+
rep.SymbolizeReport();
551557
OutputReport(thr, rep);
552558
}
553559

554560
void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
555561
FastState last_lock, StackID creation_stack_id) {
556-
// We need to lock the slot during RestoreStack because it protects
557-
// the slot journal.
558-
Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx);
559-
ThreadRegistryLock l0(&ctx->thread_registry);
560-
Lock slots_lock(&ctx->slot_mtx);
561562
ScopedReport rep(ReportTypeMutexDestroyLocked);
562-
rep.AddMutex(addr, creation_stack_id);
563-
VarSizeStackTrace trace;
564-
ObtainCurrentStack(thr, pc, &trace);
565-
rep.AddStack(trace, true);
566-
567-
Tid tid;
568-
DynamicMutexSet mset;
569-
uptr tag;
570-
if (!RestoreStack(EventType::kLock, last_lock.sid(), last_lock.epoch(), addr,
571-
0, kAccessWrite, &tid, &trace, mset, &tag))
572-
return;
573-
rep.AddStack(trace, true);
574-
rep.AddLocation(addr, 1);
563+
{
564+
// We need to lock the slot during RestoreStack because it protects
565+
// the slot journal.
566+
Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx);
567+
ThreadRegistryLock l0(&ctx->thread_registry);
568+
Lock slots_lock(&ctx->slot_mtx);
569+
rep.AddMutex(addr, creation_stack_id);
570+
VarSizeStackTrace trace;
571+
ObtainCurrentStack(thr, pc, &trace);
572+
rep.AddStack(trace, true);
573+
574+
Tid tid;
575+
DynamicMutexSet mset;
576+
uptr tag;
577+
if (!RestoreStack(EventType::kLock, last_lock.sid(), last_lock.epoch(), addr,
578+
0, kAccessWrite, &tid, &trace, mset, &tag))
579+
return;
580+
rep.AddStack(trace, true);
581+
rep.AddLocation(addr, 1);
582+
}
583+
rep.SymbolizeReport();
575584
OutputReport(thr, rep);
576585
}
577586

0 commit comments

Comments
 (0)