llvm/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp

//===-- sanitizer_stoptheworld_linux_libcdep.cpp --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// See sanitizer_stoptheworld.h for details.
// This implementation was inspired by Markus Gutschke's linuxthreads.cc.
//
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"

#if SANITIZER_LINUX &&                                                   \
    (defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \
     defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \
     defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64)

#include "sanitizer_stoptheworld.h"

#include "sanitizer_platform_limits_posix.h"
#include "sanitizer_atomic.h"

#include <errno.h>
#include <sched.h> // for CLONE_* definitions
#include <stddef.h>
#include <sys/prctl.h> // for PR_* definitions
#include <sys/ptrace.h> // for PTRACE_* definitions
#include <sys/types.h> // for pid_t
#include <sys/uio.h> // for iovec
#include <elf.h> // for NT_PRSTATUS
#if (defined(__aarch64__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64) && \
     !SANITIZER_ANDROID
// GLIBC 2.20+ sys/user does not include asm/ptrace.h
# include <asm/ptrace.h>
#endif
#include <sys/user.h>  // for user_regs_struct
#if SANITIZER_ANDROID && SANITIZER_MIPS
# include <asm/reg.h>  // for mips SP register in sys/user.h
#endif
#include <sys/wait.h> // for signal-related stuff

#ifdef sa_handler
# undef sa_handler
#endif

#ifdef sa_sigaction
# undef sa_sigaction
#endif

#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
#include "sanitizer_linux.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"

// Sufficiently old kernel headers don't provide this value, but we can still
// call prctl with it. If the runtime kernel is new enough, the prctl call will
// have the desired effect; if the kernel is too old, the call will error and we
// can ignore said error.
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER
#endif

// This module works by spawning a Linux task which then attaches to every
// thread in the caller process with ptrace. This suspends the threads, and
// PTRACE_GETREGS can then be used to obtain their register state. The callback
// supplied to StopTheWorld() is run in the tracer task while the threads are
// suspended.
// The tracer task must be placed in a different thread group for ptrace to
// work, so it cannot be spawned as a pthread. Instead, we use the low-level
// clone() interface (we want to share the address space with the caller
// process, so we prefer clone() over fork()).
//
// We don't use any libc functions, relying instead on direct syscalls. There
// are two reasons for this:
// 1. calling a library function while threads are suspended could cause a
// deadlock, if one of the treads happens to be holding a libc lock;
// 2. it's generally not safe to call libc functions from the tracer task,
// because clone() does not set up a thread-local storage for it. Any
// thread-local variables used by libc will be shared between the tracer task
// and the thread which spawned it.

namespace __sanitizer {

class SuspendedThreadsListLinux final : public SuspendedThreadsList {};

// Structure for passing arguments into the tracer thread.
struct TracerThreadArgument {};

// This class handles thread suspending/unsuspending in the tracer thread.
class ThreadSuspender {};

bool ThreadSuspender::SuspendThread(tid_t tid) {}

void ThreadSuspender::ResumeAllThreads() {}

void ThreadSuspender::KillAllThreads() {}

bool ThreadSuspender::SuspendAllThreads() {}

// Pointer to the ThreadSuspender instance for use in signal handler.
static ThreadSuspender *thread_suspender_instance =;

// Synchronous signals that should not be blocked.
static const int kSyncSignals[] =;

static void TracerThreadDieCallback() {}

// Signal handler to wake up suspended threads when the tracer thread dies.
static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
                                      void *uctx) {}

// Size of alternative stack for signal handlers in the tracer thread.
static const int kHandlerStackSize =;

// This function will be run as a cloned task.
static int TracerThread(void* argument) {}

class ScopedStackSpaceWithGuard {};

// We have a limitation on the stack frame size, so some stuff had to be moved
// into globals.
static __sanitizer_sigset_t blocked_sigset;
static __sanitizer_sigset_t old_sigset;

class StopTheWorldScope {};

// When sanitizer output is being redirected to file (i.e. by using log_path),
// the tracer should write to the parent's log instead of trying to open a new
// file. Alert the logging code to the fact that we have a tracer.
struct ScopedSetTracerPID {};

void StopTheWorld(StopTheWorldCallback callback, void *argument) {}

// Platform-specific methods from SuspendedThreadsList.
#if SANITIZER_ANDROID && defined(__arm__)
typedef pt_regs regs_struct;
#define REG_SP

#elif SANITIZER_LINUX && defined(__arm__)
typedef user_regs regs_struct;
#define REG_SP

#elif defined(__i386__) || defined(__x86_64__)
regs_struct;
#if defined(__i386__)
#define REG_SP
#else
#define REG_SP
#endif
#define ARCH_IOVEC_FOR_GETREGSET
// Support ptrace extensions even when compiled without required kernel support
#ifndef NT_X86_XSTATE
#define NT_X86_XSTATE
#endif
#ifndef PTRACE_GETREGSET
#define PTRACE_GETREGSET
#endif
// Compiler may use FP registers to store pointers.
static constexpr uptr kExtraRegs[] =;

#elif defined(__powerpc__) || defined(__powerpc64__)
typedef pt_regs regs_struct;
#define REG_SP

#elif defined(__mips__)
typedef struct user regs_struct;
# if SANITIZER_ANDROID
#define REG_SP
# else
#define REG_SP
# endif

#elif defined(__aarch64__)
typedef struct user_pt_regs regs_struct;
#define REG_SP
static constexpr uptr kExtraRegs[] = {0};
#define ARCH_IOVEC_FOR_GETREGSET

#elif defined(__loongarch__)
typedef struct user_pt_regs regs_struct;
#define REG_SP
static constexpr uptr kExtraRegs[] = {0};
#define ARCH_IOVEC_FOR_GETREGSET

#elif SANITIZER_RISCV64
typedef struct user_regs_struct regs_struct;
// sys/ucontext.h already defines REG_SP as 2. Undefine it first.
#undef REG_SP
#define REG_SP
static constexpr uptr kExtraRegs[] = {0};
#define ARCH_IOVEC_FOR_GETREGSET

#elif defined(__s390__)
typedef _user_regs_struct regs_struct;
#define REG_SP
static constexpr uptr kExtraRegs[] = {0};
#define ARCH_IOVEC_FOR_GETREGSET

#else
#error "Unsupported architecture"
#endif // SANITIZER_ANDROID && defined(__arm__)

tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const {}

uptr SuspendedThreadsListLinux::ThreadCount() const {}

bool SuspendedThreadsListLinux::ContainsTid(tid_t thread_id) const {}

void SuspendedThreadsListLinux::Append(tid_t tid) {}

PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
    uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {}

} // namespace __sanitizer

#endif  // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
        // || defined(__aarch64__) || defined(__powerpc64__)
        // || defined(__s390__) || defined(__i386__) || defined(__arm__)
        // || SANITIZER_LOONGARCH64