From 69931c584141cf4c3c251ae4fa6a843935ed79bf Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Mon, 2 Sep 2013 11:36:19 +0000 Subject: [PATCH] [sanitizer_common] Add internal_clone(). Add a wrapper for the clone syscall for use in StopTheWorld. We implement it only for x86_64, so stop building StopTheWorld for other platforms (no one uses it outside x86_64 anyway). See https://code.google.com/p/address-sanitizer/issues/detail?id=214 for why we can't use the glibc clone() wrapper. llvm-svn: 189753 --- .../lib/sanitizer_common/sanitizer_linux.cc | 63 +++++++++++++++++++ .../lib/sanitizer_common/sanitizer_linux.h | 4 ++ .../sanitizer_stoptheworld_linux_libcdep.cc | 25 ++++---- .../tests/sanitizer_stoptheworld_test.cc | 4 +- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index c7691d3a4ed4..7e786c27b45e 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -679,6 +679,69 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { } #endif +#if defined(__x86_64__) +// We cannot use glibc's clone wrapper, because it messes with the child +// task's TLS. It writes the PID and TID of the child task to its thread +// descriptor, but in our case the child task shares the thread descriptor with +// the parent (because we don't know how to allocate a new thread +// descriptor to keep glibc happy). So the stock version of clone(), when +// used with CLONE_VM, would end up corrupting the parent's thread descriptor. +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + long long res; + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(void *); + ((void **)child_stack)[0] = (void *)(uptr)fn; + ((void **)child_stack)[1] = arg; + __asm__ __volatile__( + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %6,%%r8\n" + "movq %7,%%r10\n" + ".cfi_endproc\n" + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate unwind chain. */ + ".cfi_startproc\n" + ".cfi_undefined %%rip;\n" + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%rax). */ + "movq %%rax,%%rdi\n" + "movq %2,%%rax\n" + "syscall\n" + + /* Return to parent. */ + "1:\n" + : "=a" (res) + : "a"(__NR_clone), "i"(__NR_exit), + "S"(child_stack), + "D"(flags), + "d"(parent_tidptr), + "r"(newtls), + "r"(child_tidptr) + : "rsp", "memory", "r8", "r10", "r11", "rcx"); + return res; +} +#endif // defined(__x86_64__) } // namespace __sanitizer #endif // SANITIZER_LINUX diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index edb95fecb8c5..c0fba48da137 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -29,6 +29,10 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); uptr internal_sigaltstack(const struct sigaltstack* ss, struct sigaltstack* oss); +#ifdef __x86_64__ +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); +#endif // This class reads thread IDs from /proc//task using only syscalls. class ThreadLister { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index bcaf12a2f2d1..3c650be5c578 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -14,12 +14,12 @@ #include "sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_stoptheworld.h" #include -#include // for clone +#include // for CLONE_* definitions #include #include // for PR_* definitions #include // for PTRACE_* definitions @@ -71,7 +71,6 @@ // after it has exited. The following functions are used in this manner: // sigdelset() // sigprocmask() -// clone() COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); @@ -371,11 +370,14 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // Block the execution of TracerThread until after we have set ptrace // permissions. tracer_thread_argument.mutex.Lock(); - pid_t tracer_pid = clone(TracerThread, tracer_stack.Bottom(), - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, - &tracer_thread_argument); - if (tracer_pid < 0) { - Report("Failed spawning a tracer thread (errno %d).\n", errno); + uptr tracer_pid = internal_clone( + TracerThread, tracer_stack.Bottom(), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, + &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 + /* child_tidptr */); + int local_errno = 0; + if (internal_iserror(tracer_pid, &local_errno)) { + Report("Failed spawning a tracer thread (errno %d).\n", local_errno); tracer_thread_argument.mutex.Unlock(); } else { // On some systems we have to explicitly declare that we want to be traced @@ -390,9 +392,8 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // At this point, any signal will either be blocked or kill us, so waitpid // should never return (and set errno) while the tracer thread is alive. uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); - int wperrno; - if (internal_iserror(waitpid_status, &wperrno)) - Report("Waiting on the tracer thread failed (errno %d).\n", wperrno); + if (internal_iserror(waitpid_status, &local_errno)) + Report("Waiting on the tracer thread failed (errno %d).\n", local_errno); } } @@ -448,4 +449,4 @@ uptr SuspendedThreadsList::RegisterCount() { } } // namespace __sanitizer -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc index a5f8516df575..b6786ba8b013 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_common/sanitizer_stoptheworld.h" #include "gtest/gtest.h" @@ -191,4 +191,4 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) { } // namespace __sanitizer -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && defined(__x86_64__) -- GitLab