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, &regs, 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(&regs);
354353944Sdim  internal_memcpy(buffer, &regs, 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