[compiler-rt] r230631 - LSan: fix a deadlock caused by dl_iterate_phdr().
Dmitry Vyukov
dvyukov at google.com
Thu Feb 26 06:24:44 PST 2015
On Thu, Feb 26, 2015 at 5:01 PM, Sergey Matveev <earthdok at google.com> wrote:
> Author: smatveev
> Date: Thu Feb 26 08:01:08 2015
> New Revision: 230631
>
> URL: http://llvm.org/viewvc/llvm-project?rev=230631&view=rev
> Log:
> LSan: fix a deadlock caused by dl_iterate_phdr().
>
> Wrap the StopTheWorld call in a dl_iterate_phdr() callback. This ensures that no
> other threads are holding the libdl lock, and we can safely reenter it in the
> tracer thread.
>
> Modified:
> compiler-rt/trunk/lib/lsan/lsan_common.cc
> compiler-rt/trunk/lib/lsan/lsan_common.h
> compiler-rt/trunk/lib/lsan/lsan_common_linux.cc
>
> Modified: compiler-rt/trunk/lib/lsan/lsan_common.cc
> URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=230631&r1=230630&r2=230631&view=diff
> ==============================================================================
> --- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
> +++ compiler-rt/trunk/lib/lsan/lsan_common.cc Thu Feb 26 08:01:08 2015
> @@ -21,7 +21,6 @@
> #include "sanitizer_common/sanitizer_procmaps.h"
> #include "sanitizer_common/sanitizer_stackdepot.h"
> #include "sanitizer_common/sanitizer_stacktrace.h"
> -#include "sanitizer_common/sanitizer_stoptheworld.h"
> #include "sanitizer_common/sanitizer_suppressions.h"
> #include "sanitizer_common/sanitizer_report_decorator.h"
>
> @@ -400,7 +399,7 @@ void DoLeakCheck() {
> param.success = false;
> LockThreadRegistry();
> LockAllocator();
> - StopTheWorld(DoLeakCheckCallback, ¶m);
> + DoStopTheWorld(DoLeakCheckCallback, ¶m);
> UnlockAllocator();
> UnlockThreadRegistry();
>
>
> Modified: compiler-rt/trunk/lib/lsan/lsan_common.h
> URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.h?rev=230631&r1=230630&r2=230631&view=diff
> ==============================================================================
> --- compiler-rt/trunk/lib/lsan/lsan_common.h (original)
> +++ compiler-rt/trunk/lib/lsan/lsan_common.h Thu Feb 26 08:01:08 2015
> @@ -19,6 +19,7 @@
> #include "sanitizer_common/sanitizer_common.h"
> #include "sanitizer_common/sanitizer_internal_defs.h"
> #include "sanitizer_common/sanitizer_platform.h"
> +#include "sanitizer_common/sanitizer_stoptheworld.h"
Include this in lsan_common_linux.cc, as it is used only there.
> #include "sanitizer_common/sanitizer_symbolizer.h"
>
> #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \
> @@ -99,6 +100,8 @@ typedef InternalMmapVector<uptr> Frontie
> void InitializePlatformSpecificModules();
> void ProcessGlobalRegions(Frontier *frontier);
> void ProcessPlatformSpecificAllocations(Frontier *frontier);
> +// Run stoptheworld while holding any platform-specific locks.
> +void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
>
> void ScanRangeForPointers(uptr begin, uptr end,
> Frontier *frontier,
>
> Modified: compiler-rt/trunk/lib/lsan/lsan_common_linux.cc
> URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common_linux.cc?rev=230631&r1=230630&r2=230631&view=diff
> ==============================================================================
> --- compiler-rt/trunk/lib/lsan/lsan_common_linux.cc (original)
> +++ compiler-rt/trunk/lib/lsan/lsan_common_linux.cc Thu Feb 26 08:01:08 2015
> @@ -85,10 +85,6 @@ static int ProcessGlobalRegionsCallback(
> // 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);
> }
>
> @@ -153,5 +149,30 @@ void ProcessPlatformSpecificAllocations(
> ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
> }
>
> +struct DoStopTheWorldParam {
> + StopTheWorldCallback callback;
> + void *argument;
> +};
> +
> +static int DoStopTheWorldCallback(struct dl_phdr_info *info,
> + size_t size, void *data) {
align parameters with opening brace ;)
> + 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
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list