1 //===-- sanitizer_stoptheworld_netbsd_libcdep.cpp -------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // See sanitizer_stoptheworld.h for details.
10 // This implementation was inspired by Markus Gutschke's linuxthreads.cc.
12 // This is a NetBSD variation of Linux stoptheworld implementation
13 // See sanitizer_stoptheworld_linux_libcdep.cpp for code comments.
15 //===----------------------------------------------------------------------===//
17 #include "sanitizer_platform.h"
21 #include "sanitizer_stoptheworld.h"
23 #include "sanitizer_atomic.h"
24 #include "sanitizer_platform_limits_posix.h"
26 #include <sys/types.h>
28 #include <sys/ptrace.h>
32 #include <machine/reg.h>
40 #define internal_sigaction_norestorer internal_sigaction
42 #include "sanitizer_common.h"
43 #include "sanitizer_flags.h"
44 #include "sanitizer_libc.h"
45 #include "sanitizer_linux.h"
46 #include "sanitizer_mutex.h"
47 #include "sanitizer_placement_new.h"
49 namespace __sanitizer
{
51 class SuspendedThreadsListNetBSD final
: public SuspendedThreadsList
{
53 SuspendedThreadsListNetBSD() { thread_ids_
.reserve(1024); }
55 tid_t
GetThreadID(uptr index
) const;
56 uptr
ThreadCount() const;
57 bool ContainsTid(tid_t thread_id
) const;
58 void Append(tid_t tid
);
60 PtraceRegistersStatus
GetRegistersAndSP(uptr index
,
61 InternalMmapVector
<uptr
> *buffer
,
65 InternalMmapVector
<tid_t
> thread_ids_
;
68 struct TracerThreadArgument
{
69 StopTheWorldCallback callback
;
70 void *callback_argument
;
72 atomic_uintptr_t done
;
76 class ThreadSuspender
{
78 explicit ThreadSuspender(pid_t pid
, TracerThreadArgument
*arg
)
79 : arg(arg
), pid_(pid
) {
82 bool SuspendAllThreads();
83 void ResumeAllThreads();
84 void KillAllThreads();
85 SuspendedThreadsListNetBSD
&suspended_threads_list() {
86 return suspended_threads_list_
;
88 TracerThreadArgument
*arg
;
91 SuspendedThreadsListNetBSD suspended_threads_list_
;
95 void ThreadSuspender::ResumeAllThreads() {
97 if (!internal_iserror(internal_ptrace(PT_DETACH
, pid_
, (void *)(uptr
)1, 0),
99 VReport(2, "Detached from process %d.\n", pid_
);
101 VReport(1, "Could not detach from process %d (errno %d).\n", pid_
, pterrno
);
105 void ThreadSuspender::KillAllThreads() {
106 internal_ptrace(PT_KILL
, pid_
, nullptr, 0);
109 bool ThreadSuspender::SuspendAllThreads() {
111 if (internal_iserror(internal_ptrace(PT_ATTACH
, pid_
, nullptr, 0),
113 Printf("Could not attach to process %d (errno %d).\n", pid_
, pterrno
);
119 HANDLE_EINTR(waitpid_status
, internal_waitpid(pid_
, &status
, 0));
121 VReport(2, "Attached to process %d.\n", pid_
);
124 struct ptrace_lwpstatus pl
;
127 struct ptrace_lwpinfo pl
;
134 while ((val
= internal_ptrace(op
, pid_
, (void *)&pl
, sizeof(pl
))) != -1 &&
136 suspended_threads_list_
.Append(pl
.pl_lwpid
);
137 VReport(2, "Appended thread %d in process %d.\n", pl
.pl_lwpid
, pid_
);
142 // Pointer to the ThreadSuspender instance for use in signal handler.
143 static ThreadSuspender
*thread_suspender_instance
= nullptr;
145 // Synchronous signals that should not be blocked.
146 static const int kSyncSignals
[] = {SIGABRT
, SIGILL
, SIGFPE
, SIGSEGV
,
147 SIGBUS
, SIGXCPU
, SIGXFSZ
};
149 static void TracerThreadDieCallback() {
150 ThreadSuspender
*inst
= thread_suspender_instance
;
151 if (inst
&& stoptheworld_tracer_pid
== internal_getpid()) {
152 inst
->KillAllThreads();
153 thread_suspender_instance
= nullptr;
157 // Signal handler to wake up suspended threads when the tracer thread dies.
158 static void TracerThreadSignalHandler(int signum
, __sanitizer_siginfo
*siginfo
,
160 SignalContext
ctx(siginfo
, uctx
);
161 Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum
,
162 ctx
.addr
, ctx
.pc
, ctx
.sp
);
163 ThreadSuspender
*inst
= thread_suspender_instance
;
165 if (signum
== SIGABRT
)
166 inst
->KillAllThreads();
168 inst
->ResumeAllThreads();
169 RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback
));
170 thread_suspender_instance
= nullptr;
171 atomic_store(&inst
->arg
->done
, 1, memory_order_relaxed
);
173 internal__exit((signum
== SIGABRT
) ? 1 : 2);
176 // Size of alternative stack for signal handlers in the tracer thread.
177 static const int kHandlerStackSize
= 8192;
179 // This function will be run as a cloned task.
180 static int TracerThread(void *argument
) {
181 TracerThreadArgument
*tracer_thread_argument
=
182 (TracerThreadArgument
*)argument
;
184 // Check if parent is already dead.
185 if (internal_getppid() != tracer_thread_argument
->parent_pid
)
188 // Wait for the parent thread to finish preparations.
189 tracer_thread_argument
->mutex
.Lock();
190 tracer_thread_argument
->mutex
.Unlock();
192 RAW_CHECK(AddDieCallback(TracerThreadDieCallback
));
194 ThreadSuspender
thread_suspender(internal_getppid(), tracer_thread_argument
);
195 // Global pointer for the signal handler.
196 thread_suspender_instance
= &thread_suspender
;
198 // Alternate stack for signal handling.
199 InternalMmapVector
<char> handler_stack_memory(kHandlerStackSize
);
200 stack_t handler_stack
;
201 internal_memset(&handler_stack
, 0, sizeof(handler_stack
));
202 handler_stack
.ss_sp
= handler_stack_memory
.data();
203 handler_stack
.ss_size
= kHandlerStackSize
;
204 internal_sigaltstack(&handler_stack
, nullptr);
206 // Install our handler for synchronous signals. Other signals should be
207 // blocked by the mask we inherited from the parent thread.
208 for (uptr i
= 0; i
< ARRAY_SIZE(kSyncSignals
); i
++) {
209 __sanitizer_sigaction act
;
210 internal_memset(&act
, 0, sizeof(act
));
211 act
.sigaction
= TracerThreadSignalHandler
;
212 act
.sa_flags
= SA_ONSTACK
| SA_SIGINFO
;
213 internal_sigaction_norestorer(kSyncSignals
[i
], &act
, 0);
217 if (!thread_suspender
.SuspendAllThreads()) {
218 VReport(1, "Failed suspending threads.\n");
221 tracer_thread_argument
->callback(thread_suspender
.suspended_threads_list(),
222 tracer_thread_argument
->callback_argument
);
223 thread_suspender
.ResumeAllThreads();
226 RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback
));
227 thread_suspender_instance
= nullptr;
228 atomic_store(&tracer_thread_argument
->done
, 1, memory_order_relaxed
);
232 class ScopedStackSpaceWithGuard
{
234 explicit ScopedStackSpaceWithGuard(uptr stack_size
) {
235 stack_size_
= stack_size
;
236 guard_size_
= GetPageSizeCached();
237 // FIXME: Omitting MAP_STACK here works in current kernels but might break
240 (uptr
)MmapOrDie(stack_size_
+ guard_size_
, "ScopedStackWithGuard");
241 CHECK(MprotectNoAccess((uptr
)guard_start_
, guard_size_
));
243 ~ScopedStackSpaceWithGuard() {
244 UnmapOrDie((void *)guard_start_
, stack_size_
+ guard_size_
);
246 void *Bottom() const {
247 return (void *)(guard_start_
+ stack_size_
+ guard_size_
);
256 static __sanitizer_sigset_t blocked_sigset
;
257 static __sanitizer_sigset_t old_sigset
;
259 struct ScopedSetTracerPID
{
260 explicit ScopedSetTracerPID(uptr tracer_pid
) {
261 stoptheworld_tracer_pid
= tracer_pid
;
262 stoptheworld_tracer_ppid
= internal_getpid();
264 ~ScopedSetTracerPID() {
265 stoptheworld_tracer_pid
= 0;
266 stoptheworld_tracer_ppid
= 0;
270 void StopTheWorld(StopTheWorldCallback callback
, void *argument
) {
271 // Prepare the arguments for TracerThread.
272 struct TracerThreadArgument tracer_thread_argument
;
273 tracer_thread_argument
.callback
= callback
;
274 tracer_thread_argument
.callback_argument
= argument
;
275 tracer_thread_argument
.parent_pid
= internal_getpid();
276 atomic_store(&tracer_thread_argument
.done
, 0, memory_order_relaxed
);
277 const uptr kTracerStackSize
= 2 * 1024 * 1024;
278 ScopedStackSpaceWithGuard
tracer_stack(kTracerStackSize
);
280 tracer_thread_argument
.mutex
.Lock();
282 internal_sigfillset(&blocked_sigset
);
283 for (uptr i
= 0; i
< ARRAY_SIZE(kSyncSignals
); i
++)
284 internal_sigdelset(&blocked_sigset
, kSyncSignals
[i
]);
285 int rv
= internal_sigprocmask(SIG_BLOCK
, &blocked_sigset
, &old_sigset
);
287 uptr tracer_pid
= internal_clone(TracerThread
, tracer_stack
.Bottom(),
288 CLONE_VM
| CLONE_FS
| CLONE_FILES
,
289 &tracer_thread_argument
);
290 internal_sigprocmask(SIG_SETMASK
, &old_sigset
, 0);
292 if (internal_iserror(tracer_pid
, &local_errno
)) {
293 VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno
);
294 tracer_thread_argument
.mutex
.Unlock();
296 ScopedSetTracerPID
scoped_set_tracer_pid(tracer_pid
);
298 tracer_thread_argument
.mutex
.Unlock();
300 while (atomic_load(&tracer_thread_argument
.done
, memory_order_relaxed
) == 0)
304 uptr waitpid_status
= internal_waitpid(tracer_pid
, nullptr, __WALL
);
305 if (!internal_iserror(waitpid_status
, &local_errno
))
307 if (local_errno
== EINTR
)
309 VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
316 tid_t
SuspendedThreadsListNetBSD::GetThreadID(uptr index
) const {
317 CHECK_LT(index
, thread_ids_
.size());
318 return thread_ids_
[index
];
321 uptr
SuspendedThreadsListNetBSD::ThreadCount() const {
322 return thread_ids_
.size();
325 bool SuspendedThreadsListNetBSD::ContainsTid(tid_t thread_id
) const {
326 for (uptr i
= 0; i
< thread_ids_
.size(); i
++) {
327 if (thread_ids_
[i
] == thread_id
)
333 void SuspendedThreadsListNetBSD::Append(tid_t tid
) {
334 thread_ids_
.push_back(tid
);
337 PtraceRegistersStatus
SuspendedThreadsListNetBSD::GetRegistersAndSP(
338 uptr index
, InternalMmapVector
<uptr
> *buffer
, uptr
*sp
) const {
339 lwpid_t tid
= GetThreadID(index
);
340 pid_t ppid
= internal_getppid();
344 internal_iserror(internal_ptrace(PT_GETREGS
, ppid
, ®s
, tid
), &pterrno
);
347 "Could not get registers from process %d thread %d (errno %d).\n",
349 return pterrno
== ESRCH
? REGISTERS_UNAVAILABLE_FATAL
350 : REGISTERS_UNAVAILABLE
;
353 *sp
= PTRACE_REG_SP(®s
);
354 buffer
->resize(RoundUpTo(sizeof(regs
), sizeof(uptr
)) / sizeof(uptr
));
355 internal_memcpy(buffer
->data(), ®s
, sizeof(regs
));
357 return REGISTERS_AVAILABLE
;
360 } // namespace __sanitizer