1353944Sdim//===-- sanitizer_stoptheworld_netbsd_libcdep.cpp -------------------------===// 2353944Sdim// 3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353944Sdim// See https://llvm.org/LICENSE.txt for license information. 5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353944Sdim// 7353944Sdim//===----------------------------------------------------------------------===// 8353944Sdim// 9353944Sdim// See sanitizer_stoptheworld.h for details. 10353944Sdim// This implementation was inspired by Markus Gutschke's linuxthreads.cc. 11353944Sdim// 12353944Sdim// This is a NetBSD variation of Linux stoptheworld implementation 13353944Sdim// See sanitizer_stoptheworld_linux_libcdep.cpp for code comments. 14353944Sdim// 15353944Sdim//===----------------------------------------------------------------------===// 16353944Sdim 17353944Sdim#include "sanitizer_platform.h" 18353944Sdim 19353944Sdim#if SANITIZER_NETBSD 20353944Sdim 21353944Sdim#include "sanitizer_stoptheworld.h" 22353944Sdim 23353944Sdim#include "sanitizer_atomic.h" 24353944Sdim#include "sanitizer_platform_limits_posix.h" 25353944Sdim 26353944Sdim#include <sys/types.h> 27353944Sdim 28353944Sdim#include <sys/ptrace.h> 29353944Sdim#include <sys/uio.h> 30353944Sdim#include <sys/wait.h> 31353944Sdim 32353944Sdim#include <machine/reg.h> 33353944Sdim 34353944Sdim#include <elf.h> 35353944Sdim#include <errno.h> 36353944Sdim#include <sched.h> 37353944Sdim#include <signal.h> 38353944Sdim#include <stddef.h> 39353944Sdim 40353944Sdim#define internal_sigaction_norestorer internal_sigaction 41353944Sdim 42353944Sdim#include "sanitizer_common.h" 43353944Sdim#include "sanitizer_flags.h" 44353944Sdim#include "sanitizer_libc.h" 45353944Sdim#include "sanitizer_linux.h" 46353944Sdim#include "sanitizer_mutex.h" 47353944Sdim#include "sanitizer_placement_new.h" 48353944Sdim 49353944Sdimnamespace __sanitizer { 50353944Sdim 51353944Sdimclass SuspendedThreadsListNetBSD : public SuspendedThreadsList { 52353944Sdim public: 53353944Sdim SuspendedThreadsListNetBSD() { thread_ids_.reserve(1024); } 54353944Sdim 55353944Sdim tid_t GetThreadID(uptr index) const; 56353944Sdim uptr ThreadCount() const; 57353944Sdim bool ContainsTid(tid_t thread_id) const; 58353944Sdim void Append(tid_t tid); 59353944Sdim 60353944Sdim PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer, 61353944Sdim uptr *sp) const; 62353944Sdim uptr RegisterCount() const; 63353944Sdim 64353944Sdim private: 65353944Sdim InternalMmapVector<tid_t> thread_ids_; 66353944Sdim}; 67353944Sdim 68353944Sdimstruct TracerThreadArgument { 69353944Sdim StopTheWorldCallback callback; 70353944Sdim void *callback_argument; 71353944Sdim BlockingMutex mutex; 72353944Sdim atomic_uintptr_t done; 73353944Sdim uptr parent_pid; 74353944Sdim}; 75353944Sdim 76353944Sdimclass ThreadSuspender { 77353944Sdim public: 78353944Sdim explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg) 79353944Sdim : arg(arg), pid_(pid) { 80353944Sdim CHECK_GE(pid, 0); 81353944Sdim } 82353944Sdim bool SuspendAllThreads(); 83353944Sdim void ResumeAllThreads(); 84353944Sdim void KillAllThreads(); 85353944Sdim SuspendedThreadsListNetBSD &suspended_threads_list() { 86353944Sdim return suspended_threads_list_; 87353944Sdim } 88353944Sdim TracerThreadArgument *arg; 89353944Sdim 90353944Sdim private: 91353944Sdim SuspendedThreadsListNetBSD suspended_threads_list_; 92353944Sdim pid_t pid_; 93353944Sdim}; 94353944Sdim 95353944Sdimvoid ThreadSuspender::ResumeAllThreads() { 96353944Sdim int pterrno; 97353944Sdim if (!internal_iserror(internal_ptrace(PT_DETACH, pid_, (void *)(uptr)1, 0), 98353944Sdim &pterrno)) { 99353944Sdim VReport(2, "Detached from process %d.\n", pid_); 100353944Sdim } else { 101353944Sdim VReport(1, "Could not detach from process %d (errno %d).\n", pid_, pterrno); 102353944Sdim } 103353944Sdim} 104353944Sdim 105353944Sdimvoid ThreadSuspender::KillAllThreads() { 106353944Sdim internal_ptrace(PT_KILL, pid_, nullptr, 0); 107353944Sdim} 108353944Sdim 109353944Sdimbool ThreadSuspender::SuspendAllThreads() { 110353944Sdim int pterrno; 111353944Sdim if (internal_iserror(internal_ptrace(PT_ATTACH, pid_, nullptr, 0), 112353944Sdim &pterrno)) { 113353944Sdim Printf("Could not attach to process %d (errno %d).\n", pid_, pterrno); 114353944Sdim return false; 115353944Sdim } 116353944Sdim 117353944Sdim int status; 118353944Sdim uptr waitpid_status; 119353944Sdim HANDLE_EINTR(waitpid_status, internal_waitpid(pid_, &status, 0)); 120353944Sdim 121353944Sdim VReport(2, "Attached to process %d.\n", pid_); 122353944Sdim 123357095Sdim#ifdef PT_LWPNEXT 124357095Sdim struct ptrace_lwpstatus pl; 125357095Sdim int op = PT_LWPNEXT; 126357095Sdim#else 127353944Sdim struct ptrace_lwpinfo pl; 128357095Sdim int op = PT_LWPINFO; 129357095Sdim#endif 130357095Sdim 131357095Sdim pl.pl_lwpid = 0; 132357095Sdim 133353944Sdim int val; 134357095Sdim while ((val = ptrace(op, pid_, (void *)&pl, sizeof(pl))) != -1 && 135353944Sdim pl.pl_lwpid != 0) { 136353944Sdim suspended_threads_list_.Append(pl.pl_lwpid); 137353944Sdim VReport(2, "Appended thread %d in process %d.\n", pl.pl_lwpid, pid_); 138353944Sdim } 139353944Sdim return true; 140353944Sdim} 141353944Sdim 142353944Sdim// Pointer to the ThreadSuspender instance for use in signal handler. 143353944Sdimstatic ThreadSuspender *thread_suspender_instance = nullptr; 144353944Sdim 145353944Sdim// Synchronous signals that should not be blocked. 146353944Sdimstatic const int kSyncSignals[] = {SIGABRT, SIGILL, SIGFPE, SIGSEGV, 147353944Sdim SIGBUS, SIGXCPU, SIGXFSZ}; 148353944Sdim 149353944Sdimstatic void TracerThreadDieCallback() { 150353944Sdim ThreadSuspender *inst = thread_suspender_instance; 151353944Sdim if (inst && stoptheworld_tracer_pid == internal_getpid()) { 152353944Sdim inst->KillAllThreads(); 153353944Sdim thread_suspender_instance = nullptr; 154353944Sdim } 155353944Sdim} 156353944Sdim 157353944Sdim// Signal handler to wake up suspended threads when the tracer thread dies. 158353944Sdimstatic void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo, 159353944Sdim void *uctx) { 160353944Sdim SignalContext ctx(siginfo, uctx); 161353944Sdim Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum, 162353944Sdim ctx.addr, ctx.pc, ctx.sp); 163353944Sdim ThreadSuspender *inst = thread_suspender_instance; 164353944Sdim if (inst) { 165353944Sdim if (signum == SIGABRT) 166353944Sdim inst->KillAllThreads(); 167353944Sdim else 168353944Sdim inst->ResumeAllThreads(); 169353944Sdim RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); 170353944Sdim thread_suspender_instance = nullptr; 171353944Sdim atomic_store(&inst->arg->done, 1, memory_order_relaxed); 172353944Sdim } 173353944Sdim internal__exit((signum == SIGABRT) ? 1 : 2); 174353944Sdim} 175353944Sdim 176353944Sdim// Size of alternative stack for signal handlers in the tracer thread. 177353944Sdimstatic const int kHandlerStackSize = 8192; 178353944Sdim 179353944Sdim// This function will be run as a cloned task. 180353944Sdimstatic int TracerThread(void *argument) { 181353944Sdim TracerThreadArgument *tracer_thread_argument = 182353944Sdim (TracerThreadArgument *)argument; 183353944Sdim 184353944Sdim // Check if parent is already dead. 185353944Sdim if (internal_getppid() != tracer_thread_argument->parent_pid) 186353944Sdim internal__exit(4); 187353944Sdim 188353944Sdim // Wait for the parent thread to finish preparations. 189353944Sdim tracer_thread_argument->mutex.Lock(); 190353944Sdim tracer_thread_argument->mutex.Unlock(); 191353944Sdim 192353944Sdim RAW_CHECK(AddDieCallback(TracerThreadDieCallback)); 193353944Sdim 194353944Sdim ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument); 195353944Sdim // Global pointer for the signal handler. 196353944Sdim thread_suspender_instance = &thread_suspender; 197353944Sdim 198353944Sdim // Alternate stack for signal handling. 199353944Sdim InternalMmapVector<char> handler_stack_memory(kHandlerStackSize); 200353944Sdim stack_t handler_stack; 201353944Sdim internal_memset(&handler_stack, 0, sizeof(handler_stack)); 202353944Sdim handler_stack.ss_sp = handler_stack_memory.data(); 203353944Sdim handler_stack.ss_size = kHandlerStackSize; 204353944Sdim internal_sigaltstack(&handler_stack, nullptr); 205353944Sdim 206353944Sdim // Install our handler for synchronous signals. Other signals should be 207353944Sdim // blocked by the mask we inherited from the parent thread. 208353944Sdim for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) { 209353944Sdim __sanitizer_sigaction act; 210353944Sdim internal_memset(&act, 0, sizeof(act)); 211353944Sdim act.sigaction = TracerThreadSignalHandler; 212353944Sdim act.sa_flags = SA_ONSTACK | SA_SIGINFO; 213353944Sdim internal_sigaction_norestorer(kSyncSignals[i], &act, 0); 214353944Sdim } 215353944Sdim 216353944Sdim int exit_code = 0; 217353944Sdim if (!thread_suspender.SuspendAllThreads()) { 218353944Sdim VReport(1, "Failed suspending threads.\n"); 219353944Sdim exit_code = 3; 220353944Sdim } else { 221353944Sdim tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), 222353944Sdim tracer_thread_argument->callback_argument); 223353944Sdim thread_suspender.ResumeAllThreads(); 224353944Sdim exit_code = 0; 225353944Sdim } 226353944Sdim RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback)); 227353944Sdim thread_suspender_instance = nullptr; 228353944Sdim atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed); 229353944Sdim return exit_code; 230353944Sdim} 231353944Sdim 232353944Sdimclass ScopedStackSpaceWithGuard { 233353944Sdim public: 234353944Sdim explicit ScopedStackSpaceWithGuard(uptr stack_size) { 235353944Sdim stack_size_ = stack_size; 236353944Sdim guard_size_ = GetPageSizeCached(); 237353944Sdim // FIXME: Omitting MAP_STACK here works in current kernels but might break 238353944Sdim // in the future. 239353944Sdim guard_start_ = 240353944Sdim (uptr)MmapOrDie(stack_size_ + guard_size_, "ScopedStackWithGuard"); 241353944Sdim CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_)); 242353944Sdim } 243353944Sdim ~ScopedStackSpaceWithGuard() { 244353944Sdim UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); 245353944Sdim } 246353944Sdim void *Bottom() const { 247353944Sdim return (void *)(guard_start_ + stack_size_ + guard_size_); 248353944Sdim } 249353944Sdim 250353944Sdim private: 251353944Sdim uptr stack_size_; 252353944Sdim uptr guard_size_; 253353944Sdim uptr guard_start_; 254353944Sdim}; 255353944Sdim 256353944Sdimstatic __sanitizer_sigset_t blocked_sigset; 257353944Sdimstatic __sanitizer_sigset_t old_sigset; 258353944Sdim 259353944Sdimstruct ScopedSetTracerPID { 260353944Sdim explicit ScopedSetTracerPID(uptr tracer_pid) { 261353944Sdim stoptheworld_tracer_pid = tracer_pid; 262353944Sdim stoptheworld_tracer_ppid = internal_getpid(); 263353944Sdim } 264353944Sdim ~ScopedSetTracerPID() { 265353944Sdim stoptheworld_tracer_pid = 0; 266353944Sdim stoptheworld_tracer_ppid = 0; 267353944Sdim } 268353944Sdim}; 269353944Sdim 270353944Sdimvoid StopTheWorld(StopTheWorldCallback callback, void *argument) { 271353944Sdim // Prepare the arguments for TracerThread. 272353944Sdim struct TracerThreadArgument tracer_thread_argument; 273353944Sdim tracer_thread_argument.callback = callback; 274353944Sdim tracer_thread_argument.callback_argument = argument; 275353944Sdim tracer_thread_argument.parent_pid = internal_getpid(); 276353944Sdim atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed); 277353944Sdim const uptr kTracerStackSize = 2 * 1024 * 1024; 278353944Sdim ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); 279353944Sdim 280353944Sdim tracer_thread_argument.mutex.Lock(); 281353944Sdim 282353944Sdim internal_sigfillset(&blocked_sigset); 283353944Sdim for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) 284353944Sdim internal_sigdelset(&blocked_sigset, kSyncSignals[i]); 285353944Sdim int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); 286353944Sdim CHECK_EQ(rv, 0); 287353944Sdim uptr tracer_pid = internal_clone(TracerThread, tracer_stack.Bottom(), 288353944Sdim CLONE_VM | CLONE_FS | CLONE_FILES, 289353944Sdim &tracer_thread_argument); 290353944Sdim internal_sigprocmask(SIG_SETMASK, &old_sigset, 0); 291353944Sdim int local_errno = 0; 292353944Sdim if (internal_iserror(tracer_pid, &local_errno)) { 293353944Sdim VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno); 294353944Sdim tracer_thread_argument.mutex.Unlock(); 295353944Sdim } else { 296353944Sdim ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid); 297353944Sdim 298353944Sdim tracer_thread_argument.mutex.Unlock(); 299353944Sdim 300353944Sdim while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0) 301353944Sdim sched_yield(); 302353944Sdim 303353944Sdim for (;;) { 304353944Sdim uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL); 305353944Sdim if (!internal_iserror(waitpid_status, &local_errno)) 306353944Sdim break; 307353944Sdim if (local_errno == EINTR) 308353944Sdim continue; 309353944Sdim VReport(1, "Waiting on the tracer thread failed (errno %d).\n", 310353944Sdim local_errno); 311353944Sdim break; 312353944Sdim } 313353944Sdim } 314353944Sdim} 315353944Sdim 316353944Sdimtid_t SuspendedThreadsListNetBSD::GetThreadID(uptr index) const { 317353944Sdim CHECK_LT(index, thread_ids_.size()); 318353944Sdim return thread_ids_[index]; 319353944Sdim} 320353944Sdim 321353944Sdimuptr SuspendedThreadsListNetBSD::ThreadCount() const { 322353944Sdim return thread_ids_.size(); 323353944Sdim} 324353944Sdim 325353944Sdimbool SuspendedThreadsListNetBSD::ContainsTid(tid_t thread_id) const { 326353944Sdim for (uptr i = 0; i < thread_ids_.size(); i++) { 327353944Sdim if (thread_ids_[i] == thread_id) 328353944Sdim return true; 329353944Sdim } 330353944Sdim return false; 331353944Sdim} 332353944Sdim 333353944Sdimvoid SuspendedThreadsListNetBSD::Append(tid_t tid) { 334353944Sdim thread_ids_.push_back(tid); 335353944Sdim} 336353944Sdim 337353944SdimPtraceRegistersStatus SuspendedThreadsListNetBSD::GetRegistersAndSP( 338353944Sdim uptr index, uptr *buffer, uptr *sp) const { 339353944Sdim lwpid_t tid = GetThreadID(index); 340353944Sdim pid_t ppid = internal_getppid(); 341353944Sdim struct reg regs; 342353944Sdim int pterrno; 343353944Sdim bool isErr = 344353944Sdim internal_iserror(internal_ptrace(PT_GETREGS, ppid, ®s, tid), &pterrno); 345353944Sdim if (isErr) { 346353944Sdim VReport(1, 347353944Sdim "Could not get registers from process %d thread %d (errno %d).\n", 348353944Sdim ppid, tid, pterrno); 349353944Sdim return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL 350353944Sdim : REGISTERS_UNAVAILABLE; 351353944Sdim } 352353944Sdim 353353944Sdim *sp = PTRACE_REG_SP(®s); 354353944Sdim internal_memcpy(buffer, ®s, sizeof(regs)); 355353944Sdim 356353944Sdim return REGISTERS_AVAILABLE; 357353944Sdim} 358353944Sdim 359353944Sdimuptr SuspendedThreadsListNetBSD::RegisterCount() const { 360353944Sdim return sizeof(struct reg) / sizeof(uptr); 361353944Sdim} 362353944Sdim} // namespace __sanitizer 363353944Sdim 364353944Sdim#endif 365