diff options
Diffstat (limited to 'contrib/compiler-rt/lib/lsan/lsan_common_linux.cc')
-rw-r--r-- | contrib/compiler-rt/lib/lsan/lsan_common_linux.cc | 31 |
1 files changed, 26 insertions, 5 deletions
diff --git a/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc b/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc index ba51868..2955343 100644 --- a/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc +++ b/contrib/compiler-rt/lib/lsan/lsan_common_linux.cc @@ -85,10 +85,6 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { if (!flags()->use_globals) return; - // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of - // deadlocking by running this under StopTheWorld. However, the lock is - // reentrant, so we should be able to fix this by acquiring the lock before - // suspending threads. dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); } @@ -114,7 +110,7 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { reinterpret_cast<ProcessPlatformAllocParam *>(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); - if (m.allocated() && m.tag() != kReachable) { + if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) { u32 stack_id = m.stack_trace_id(); uptr caller_pc = 0; if (stack_id > 0) @@ -153,5 +149,30 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) { ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); } +struct DoStopTheWorldParam { + StopTheWorldCallback callback; + void *argument; +}; + +static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, + void *data) { + DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data); + StopTheWorld(param->callback, param->argument); + return 1; +} + +// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one +// of the threads is frozen while holding the libdl lock, the tracer will hang +// in dl_iterate_phdr() forever. +// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the +// tracer task and the thread that spawned it. Thus, if we run the tracer task +// while holding the libdl lock in the parent thread, we can safely reenter it +// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() +// callback in the parent thread. +void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { + DoStopTheWorldParam param = {callback, argument}; + dl_iterate_phdr(DoStopTheWorldCallback, ¶m); +} + } // namespace __lsan #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX |