Skip to content
Snippets Groups Projects
Commit 69931c58 authored by Sergey Matveev's avatar Sergey Matveev
Browse files

[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
parent 2bbc7903
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -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/<pid>/task using only syscalls.
class ThreadLister {
......
......@@ -14,12 +14,12 @@
#include "sanitizer_platform.h"
#if SANITIZER_LINUX
#if SANITIZER_LINUX && defined(__x86_64__)
#include "sanitizer_stoptheworld.h"
#include <errno.h>
#include <sched.h> // for clone
#include <sched.h> // for CLONE_* definitions
#include <stddef.h>
#include <sys/prctl.h> // for PR_* definitions
#include <sys/ptrace.h> // 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__)
......@@ -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__)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment