Page MenuHomeFreeBSD

D44030.diff
No OneTemporary

D44030.diff

diff --git a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common.h b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common.h
--- a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common.h
+++ b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common.h
@@ -48,7 +48,7 @@
# define CAN_SANITIZE_LEAKS 1
#elif SANITIZER_RISCV64 && SANITIZER_LINUX
# define CAN_SANITIZE_LEAKS 1
-#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA
+#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA || SANITIZER_FREEBSD
# define CAN_SANITIZE_LEAKS 1
#else
# define CAN_SANITIZE_LEAKS 0
diff --git a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp
--- a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp
+++ b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_common_linux.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This file is a part of LeakSanitizer.
-// Implementation of common leak checking functionality. Linux/NetBSD-specific
+// Implementation of common leak checking functionality. Linux/NetBSD/FreeBSD-specific
// code.
//
//===----------------------------------------------------------------------===//
@@ -15,7 +15,7 @@
#include "sanitizer_common/sanitizer_platform.h"
#include "lsan_common.h"
-#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD)
+#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FREEBSD)
#include <link.h>
#include "sanitizer_common/sanitizer_common.h"
@@ -139,7 +139,16 @@
void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
CheckForLeaksParam *argument) {
DoStopTheWorldParam param = {callback, argument};
+#if SANITIZER_FREEBSD
+ // The above deadlock is plausible on FreeBSD as well, but it needs to be
+ // solved differently there. The lock is reentrant if libthr is not used, but
+ // libthr intentionally breaks recursion detection because of some signal
+ // safety concerns. Future work might be able to fix the deadlock probability
+ // in StopTheWorld with a pthread-based solution.
+ LockStuffAndStopTheWorldCallback(0, 0, &param);
+#else
dl_iterate_phdr(LockStuffAndStopTheWorldCallback, &param);
+#endif
}
} // namespace __lsan
diff --git a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_linux.cpp b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_linux.cpp
--- a/contrib/llvm-project/compiler-rt/lib/lsan/lsan_linux.cpp
+++ b/contrib/llvm-project/compiler-rt/lib/lsan/lsan_linux.cpp
@@ -12,7 +12,7 @@
#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA
+#if SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA || SANITIZER_FREEBSD
# include "lsan_allocator.h"
# include "lsan_thread.h"
diff --git a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
--- a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -770,10 +770,17 @@
#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// Syscall wrappers.
+#if SANITIZER_FREEBSD
+uptr internal_ptrace(int request, int pid, void *addr, int data) {
+ return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
+ data);
+}
+#else
uptr internal_ptrace(int request, int pid, void *addr, void *data) {
return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
(uptr)data);
}
+#endif
uptr internal_waitpid(int pid, int *status, int options) {
return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
diff --git a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_posix.h b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_posix.h
--- a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_posix.h
+++ b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_posix.h
@@ -56,7 +56,7 @@
uptr internal_rename(const char *oldpath, const char *newpath);
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
-#if SANITIZER_NETBSD
+#if SANITIZER_NETBSD || SANITIZER_FREEBSD
uptr internal_ptrace(int request, int pid, void *addr, int data);
#else
uptr internal_ptrace(int request, int pid, void *addr, void *data);
diff --git a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_freebsd_libcdep.cpp b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_freebsd_libcdep.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_freebsd_libcdep.cpp
@@ -0,0 +1,278 @@
+//===-- sanitizer_stoptheworld_netbsd_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.
+//
+// This is a NetBSD variation of Linux stoptheworld implementation
+// See sanitizer_stoptheworld_linux_libcdep.cpp for code comments.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD
+
+#include "sanitizer_stoptheworld.h"
+
+#include <errno.h>
+#include <sys/types.h> // for pid_t
+#include <sys/ptrace.h> // for PTRACE_* definitions
+#include <sys/wait.h>
+#include <link.h>
+#include <unistd.h> // for rfork
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+
+// This module works by forking a tracer process that shares the address space
+// with the caller process, which subsequently attaches to the caller process
+// with ptrace and suspends all threads within. PTRACE_GETREGS can then be used
+// to obtain their register state. The callback supplied to StopTheWorld() is
+// run in the tracer process while the threads are suspended.
+
+namespace __sanitizer {
+
+class SuspendedThreadsListFreeBSD : public SuspendedThreadsList {
+ public:
+ SuspendedThreadsListFreeBSD(pid_t ppid) : parent_pid_(ppid), stopped_(false)
+ { lwp_ids_.reserve(1024); }
+ ~SuspendedThreadsListFreeBSD();
+ bool Suspend() { dl_iterate_phdr(Callback, this); return stopped_; }
+ void SuspendLocked();
+ static int Callback(struct dl_phdr_info *, size_t, void *);
+
+ tid_t GetThreadID(uptr index) const;
+ uptr ThreadCount() const;
+ bool ContainsTid(tid_t thread_id) const;
+
+ PtraceRegistersStatus GetRegistersAndSP(uptr index, InternalMmapVector<uptr> *buffer,
+ uptr *sp) const;
+ uptr RegisterCount() const;
+
+ private:
+ InternalMmapVector<lwpid_t> lwp_ids_;
+ pid_t parent_pid_;
+ bool stopped_;
+};
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ uptr parent_pid;
+};
+
+// Size of alternative stack for signal handlers in the tracer thread.
+static const int kHandlerStackSize = 8192;
+
+// This function will be run as a rfork'd process.
+static int TracerThread(void* argument) {
+ TracerThreadArgument *tracer_thread_argument =
+ (TracerThreadArgument *)argument;
+
+ pid_t ppid = tracer_thread_argument->parent_pid;
+ SuspendedThreadsListFreeBSD suspended_threads_list(ppid);
+
+ if (suspended_threads_list.Suspend())
+ tracer_thread_argument->callback(suspended_threads_list,
+ tracer_thread_argument->callback_argument);
+
+ return 0;
+}
+
+class ScopedStackSpaceWithGuard {
+ public:
+ explicit ScopedStackSpaceWithGuard(uptr stack_size) {
+ stack_size_ = stack_size;
+ guard_size_ = GetPageSizeCached();
+ guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
+ "ScopedStackWithGuard");
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
+ }
+ ~ScopedStackSpaceWithGuard() {
+ UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
+ }
+ void *Bottom() const {
+ return (void *)(guard_start_ + stack_size_ + guard_size_);
+ }
+
+ private:
+ uptr stack_size_;
+ uptr guard_size_;
+ uptr guard_start_;
+};
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+ // Prepare the arguments for TracerThread.
+ struct TracerThreadArgument tracer_thread_argument;
+ tracer_thread_argument.callback = callback;
+ tracer_thread_argument.callback_argument = argument;
+ tracer_thread_argument.parent_pid = internal_getpid();
+#if defined(__amd64__) || defined(__i386__)
+ const uptr kTracerStackSize = 2 * 1024 * 1024;
+ ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
+
+ uptr tracer_pid = rfork_thread(RFPROC | RFMEM, tracer_stack.Bottom(),
+ TracerThread, &tracer_thread_argument);
+#else
+ uptr tracer_pid = rfork(RFPROC | RFMEM);
+ if (tracer_pid == 0)
+ _exit(TracerThread(&tracer_thread_argument));
+#endif
+
+ int local_errno = 0;
+ if (internal_iserror(tracer_pid, &local_errno)) {
+ VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
+ } else {
+ for (;;) {
+ int status;
+ uptr waitpid_status = internal_waitpid(tracer_pid, &status, 0);
+ if (internal_iserror(waitpid_status, &local_errno)) {
+ if (local_errno == EINTR)
+ continue;
+ VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
+ local_errno);
+ break;
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ internal__exit(WEXITSTATUS(status));
+ if (WIFEXITED(status))
+ break;
+ }
+ }
+}
+
+// Platform-specific methods from SuspendedThreadsList.
+#if defined(__amd64__)
+#define PTRACE_REG_SP(regs) ((regs)->r_rsp)
+#elif defined(__i386__)
+#define PTRACE_REG_SP(regs) ((regs)->r_esp)
+#elif defined(__aarch64__) || defined(__riscv)
+#define PTRACE_REG_SP(regs) ((regs)->sp)
+#elif defined(__arm__)
+#define PTRACE_REG_SP(regs) ((regs)->r_sp)
+#elif defined(__powerpc__)
+#define PTRACE_REG_SP(regs) ((regs)->fixreg[1])
+#else
+#error "Unsupported architecture"
+#endif
+
+SuspendedThreadsListFreeBSD::~SuspendedThreadsListFreeBSD() {
+ stoptheworld_tracer_pid = 0;
+ stoptheworld_tracer_ppid = 0;
+
+ if (!stopped_)
+ return;
+
+ int local_errno = 0;
+ if (internal_iserror(internal_ptrace(PT_DETACH, parent_pid_, 0, 0),
+ &local_errno)) {
+ VReport(1, "Failed to detach the parent.\n");
+ }
+}
+
+void SuspendedThreadsListFreeBSD::SuspendLocked() {
+ stoptheworld_tracer_pid = internal_getpid();
+ stoptheworld_tracer_ppid = parent_pid_;
+
+ int local_errno = 0;
+ if (internal_iserror(internal_ptrace(PT_ATTACH, parent_pid_, 0, 0),
+ &local_errno)) {
+ VReport(1, "Failed to attach the parent.\n");
+ return;
+ }
+
+ // wait for the parent process to stop
+ for (;;) {
+ int status;
+ if (internal_iserror(internal_waitpid(parent_pid_, &status, 0),
+ &local_errno)) {
+ if (local_errno == EINTR)
+ continue;
+ VReport(1, "Failed to stop the parent (errno %d).\n", local_errno);
+ return;
+ }
+ if (WIFSTOPPED(status))
+ break;
+ }
+
+ uptr lwp_count = internal_ptrace(PT_GETNUMLWPS, parent_pid_, 0, 0);
+ if (internal_iserror(lwp_count, &local_errno)) {
+ VReport(1, "Failed to get LWP count (errno %d).\n", local_errno);
+ return;
+ }
+
+ lwp_ids_.resize(lwp_count);
+ if (internal_iserror(internal_ptrace(PT_GETLWPLIST, parent_pid_,
+ lwp_ids_.data(), lwp_ids_.size()),
+ &local_errno)) {
+ lwp_ids_.clear();
+ VReport(1, "Failed to get LWP list (errno %d).\n", local_errno);
+ return;
+ }
+
+ stopped_ = true;
+}
+
+int SuspendedThreadsListFreeBSD::Callback(struct dl_phdr_info *info,
+ size_t size, void *data)
+{
+ SuspendedThreadsListFreeBSD* self = (SuspendedThreadsListFreeBSD*)data;
+ self->SuspendLocked();
+ return 1;
+}
+
+tid_t SuspendedThreadsListFreeBSD::GetThreadID(uptr index) const {
+ CHECK_LT(index, lwp_ids_.size());
+ return lwp_ids_[index];
+}
+
+uptr SuspendedThreadsListFreeBSD::ThreadCount() const {
+ return lwp_ids_.size();
+}
+
+bool SuspendedThreadsListFreeBSD::ContainsTid(tid_t thread_id) const {
+ lwpid_t lwp_id = (lwpid_t)thread_id;
+ for (uptr i = 0; i < lwp_ids_.size(); i++) {
+ if (lwp_ids_[i] == lwp_id) return true;
+ }
+ return false;
+}
+
+PtraceRegistersStatus SuspendedThreadsListFreeBSD::GetRegistersAndSP(
+ uptr index, InternalMmapVector<uptr> *buffer, uptr *sp) const {
+ int tid = GetThreadID(index);
+ struct reg regs;
+ int pterrno;
+ bool isErr = internal_iserror(internal_ptrace(PT_GETREGS, tid,
+ (caddr_t)&regs, 0), &pterrno);
+ if (isErr) {
+ VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
+ pterrno);
+ // ESRCH means that the given thread is not suspended or already dead.
+ // Therefore it's unsafe to inspect its data (e.g. walk through stack) and
+ // we should notify caller about this.
+ return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL
+ : REGISTERS_UNAVAILABLE;
+ }
+
+ *sp = PTRACE_REG_SP(&regs);
+ buffer->resize(RoundUpTo(sizeof(regs), sizeof(uptr)) / sizeof(uptr));
+ internal_memcpy(buffer->data(), &regs, sizeof(regs));
+ return REGISTERS_AVAILABLE;
+}
+
+uptr SuspendedThreadsListFreeBSD::RegisterCount() const {
+ return sizeof(struct reg) / sizeof(uptr);
+}
+} // namespace __sanitizer
+
+#endif

File Metadata

Mime Type
text/plain
Expires
Sat, Feb 15, 7:38 PM (11 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16663981
Default Alt Text
D44030.diff (13 KB)

Event Timeline