Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -589,7 +589,7 @@ endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux") + OS_NAME MATCHES "Darwin|Linux|NetBSD") set(COMPILER_RT_HAS_LSAN TRUE) else() set(COMPILER_RT_HAS_LSAN FALSE) Index: lib/lsan/lsan_common.h =================================================================== --- lib/lsan/lsan_common.h +++ lib/lsan/lsan_common.h @@ -40,6 +40,8 @@ #elif defined(__arm__) && \ SANITIZER_LINUX && !SANITIZER_ANDROID #define CAN_SANITIZE_LEAKS 1 +#elif SANITIZER_NETBSD && defined(__x86_64__) +#define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif Index: lib/lsan/lsan_common_linux.cc =================================================================== --- lib/lsan/lsan_common_linux.cc +++ lib/lsan/lsan_common_linux.cc @@ -14,7 +14,7 @@ #include "sanitizer_common/sanitizer_platform.h" #include "lsan_common.h" -#if CAN_SANITIZE_LEAKS && SANITIZER_LINUX +#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD) #include #include "sanitizer_common/sanitizer_common.h" @@ -136,4 +136,4 @@ } // namespace __lsan -#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX +#endif Index: lib/lsan/lsan_linux.cc =================================================================== --- lib/lsan/lsan_linux.cc +++ lib/lsan/lsan_linux.cc @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// // -// This file is a part of LeakSanitizer. Linux-specific code. +// This file is a part of LeakSanitizer. Linux/NetBSD-specific code. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_LINUX || SANITIZER_NETBSD #include "lsan_allocator.h" @@ -29,4 +29,4 @@ } // namespace __lsan -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX || SANITIZER_NETBSD Index: lib/sanitizer_common/sanitizer_linux.h =================================================================== --- lib/sanitizer_common/sanitizer_linux.h +++ lib/sanitizer_common/sanitizer_linux.h @@ -67,6 +67,10 @@ #endif #elif SANITIZER_FREEBSD void internal_sigdelset(__sanitizer_sigset_t *set, int signum); +#elif SANITIZER_NETBSD +void internal_sigdelset(__sanitizer_sigset_t *set, int signum); +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); #endif // SANITIZER_LINUX // This class reads thread IDs from /proc//task using only syscalls. Index: lib/sanitizer_common/sanitizer_netbsd.cc =================================================================== --- lib/sanitizer_common/sanitizer_netbsd.cc +++ lib/sanitizer_common/sanitizer_netbsd.cc @@ -246,9 +246,8 @@ } uptr internal_ptrace(int request, int pid, void *addr, void *data) { - Printf("internal_ptrace not implemented for NetBSD"); - Die(); - return 0; + DEFINE__REAL(int, ptrace, int a, int b, void *c, int d); + return _REAL(ptrace, request, pid, addr, (int)(uptr)data); } uptr internal_waitpid(int pid, int *status, int options) { @@ -322,11 +321,16 @@ (void)_REAL(__sigemptyset14, set); } -uptr intrnal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, - int *parent_tidptr, void *newtls, int *child_tidptr) { - Printf("internal_clone not implemented for NetBSD"); - Die(); - return 0; +void internal_sigdelset(__sanitizer_sigset_t *set, int signo) { + DEFINE__REAL(int, __sigdelset14, const void *a, int b); + (void)_REAL(__sigdelset14, set, signo); +} + +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + DEFINE__REAL(int, clone, int (*a)(void *b), void *c, int d, void *e); + + return _REAL(clone, fn, child_stack, flags, arg); } } // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -13,40 +13,53 @@ #include "sanitizer_platform.h" -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) || defined(__powerpc64__) || \ - defined(__s390__) || defined(__i386__) || \ - defined(__arm__)) +#if (SANITIZER_NETBSD && defined(__x86_64__)) || \ + (SANITIZER_LINUX && \ + (defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || \ + defined(__powerpc64__) || defined(__s390__) || defined(__i386__) || \ + defined(__arm__))) #include "sanitizer_stoptheworld.h" #include "sanitizer_platform_limits_posix.h" #include "sanitizer_atomic.h" +#include // must go first on NetBSD + #include -#include // for CLONE_* definitions +#include // for CLONE_* definitions +#include #include -#include // for PR_* definitions -#include // for PTRACE_* definitions -#include // for pid_t -#include // for iovec -#include // for NT_PRSTATUS -#if defined(__aarch64__) && !SANITIZER_ANDROID +#if SANITIZER_LINUX +#include // for PR_* definitions +#include // for user_regs_struct +#endif +#include // for NT_PRSTATUS +#include // for PTRACE_* definitions +#include // for iovec +#if defined(__aarch64__) && !SANITIZER_ANDROID && SANITIZER_LINUX // GLIBC 2.20+ sys/user does not include asm/ptrace.h # include #endif -#include // for user_regs_struct #if SANITIZER_ANDROID && SANITIZER_MIPS # include // for mips SP register in sys/user.h #endif #include // for signal-related stuff +#if SANITIZER_LINUX #ifdef sa_handler -# undef sa_handler +#undef sa_handler #endif #ifdef sa_sigaction -# undef sa_sigaction +#undef sa_sigaction +#endif +#endif + +#if SANITIZER_NETBSD +#define CLONE_UNTRACED 0 +#include +#define internal_sigaction_norestorer internal_sigaction #endif #include "sanitizer_common.h" @@ -56,6 +69,7 @@ #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" +#if SANITIZER_LINUX // 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 @@ -63,6 +77,7 @@ #ifndef PR_SET_PTRACER #define PR_SET_PTRACER 0x59616d61 #endif +#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 @@ -132,9 +147,12 @@ private: SuspendedThreadsListLinux suspended_threads_list_; pid_t pid_; +#if SANITIZER_LINUX bool SuspendThread(tid_t thread_id); +#endif }; +#if SANITIZER_LINUX bool ThreadSuspender::SuspendThread(tid_t tid) { // Are we already attached to this thread? // Currently this check takes linear time, however the number of threads is @@ -169,7 +187,7 @@ // doesn't hurt to report it. VReport(1, "Waiting on thread %zu failed, detaching (errno %d).\n", (uptr)tid, wperrno); - internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr); + internal_ptrace(PTRACE_DETACH, tid, (void *)(uptr)1, nullptr); return false; } if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) { @@ -183,8 +201,20 @@ return true; } } +#endif void ThreadSuspender::ResumeAllThreads() { +#if SANITIZER_NETBSD + int pterrno; + if (!internal_iserror( + internal_ptrace(PT_DETACH, pid_, (void *)(uptr)1, nullptr), + &pterrno)) { + VReport(2, "Detached from process %d.\n", pid_); + } + { + VReport(1, "Could not detach from process %d (errno %d).\n", pid_, pterrno); + } +#else for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) { pid_t tid = suspended_threads_list_.GetThreadID(i); int pterrno; @@ -198,15 +228,44 @@ VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno); } } +#endif } void ThreadSuspender::KillAllThreads() { +#if SANITIZER_NETBSD + internal_ptrace(PT_KILL, pid_, nullptr, nullptr); +#else for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i), nullptr, nullptr); +#endif } bool ThreadSuspender::SuspendAllThreads() { +#if SANITIZER_NETBSD + int pterrno; + if (internal_iserror(internal_ptrace(PT_ATTACH, pid_, nullptr, nullptr), + &pterrno)) { + Printf("Could not attach to process %d (errno %d).\n", pid_, pterrno); + Die(); + } + + int status; + uptr waitpid_status; + HANDLE_EINTR(waitpid_status, internal_waitpid(pid_, &status, 0)); + + VReport(2, "Attached to process %d.\n", pid_); + + struct ptrace_lwpinfo pl; + int val; + pl.pl_lwpid = 0; + while ((val = ptrace(PT_LWPINFO, pid_, (void *)&pl, sizeof(pl))) != -1 && + pl.pl_lwpid != 0) { + suspended_threads_list_.Append(pl.pl_lwpid); + VReport(2, "Appended thread %d in process %d.\n", pl.pl_lwpid, pid_); + } + return true; +#else ThreadLister thread_lister(pid_); bool retry = true; InternalMmapVector threads; @@ -227,6 +286,7 @@ if (SuspendThread(tid)) retry = true; }; +#endif return suspended_threads_list_.ThreadCount(); } @@ -278,7 +338,9 @@ TracerThreadArgument *tracer_thread_argument = (TracerThreadArgument *)argument; +#ifdef PR_SET_PDEATHSIG internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); +#endif // Check if parent is already dead. if (internal_getppid() != tracer_thread_argument->parent_pid) internal__exit(4); @@ -361,19 +423,25 @@ StopTheWorldScope() { // Make this process dumpable. Processes that are not dumpable cannot be // attached to. +#ifdef PR_GET_DUMPABLE process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); +#endif } ~StopTheWorldScope() { +#ifdef PR_GET_DUMPABLE // Restore the dumpable flag. if (!process_was_dumpable_) internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); +#endif } +#ifdef PR_GET_DUMPABLE private: int process_was_dumpable_; +#endif }; // When sanitizer output is being redirected to file (i.e. by using log_path), @@ -441,7 +509,9 @@ ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid); // On some systems we have to explicitly declare that we want to be traced // by the tracer thread. +#ifdef PR_SET_PTRACER internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0); +#endif // Allow the tracer thread to start. tracer_thread_argument.mutex.Unlock(); // NOTE: errno is shared between this thread and the tracer thread. @@ -469,6 +539,7 @@ } // Platform-specific methods from SuspendedThreadsList. +#if SANITIZER_LINUX #if SANITIZER_ANDROID && defined(__arm__) typedef pt_regs regs_struct; #define REG_SP ARM_sp @@ -510,6 +581,7 @@ #else #error "Unsupported architecture" #endif // SANITIZER_ANDROID && defined(__arm__) +#endif tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const { CHECK_LT(index, thread_ids_.size()); @@ -533,10 +605,31 @@ PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP( uptr index, uptr *buffer, uptr *sp) const { +#if SANITIZER_NETBSD + int tid = GetThreadID(index); + int ppid = internal_getppid(); + struct reg regs; + int pterrno; + bool isErr = internal_iserror( + internal_ptrace(PT_GETREGS, ppid, ®s, (void *)(uptr)tid), &pterrno); + if (isErr) { + VReport(1, + "Could not get registers from process %d thread %d (errno %d).\n", + ppid, 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(®s); +#else pid_t tid = GetThreadID(index); regs_struct regs; int pterrno; -#ifdef ARCH_IOVEC_FOR_GETREGSET + bool isErr = internal_iserror( + internal_ptrace(PT_GETREGS, tid, nullptr, ®s), &pterrno); +#if defined(ARCH_IOVEC_FOR_GETREGSET) struct iovec regset_io; regset_io.iov_base = ®s; regset_io.iov_len = sizeof(regs_struct); @@ -558,15 +651,19 @@ } *sp = regs.REG_SP; +#endif + internal_memcpy(buffer, ®s, sizeof(regs)); return REGISTERS_AVAILABLE; } uptr SuspendedThreadsListLinux::RegisterCount() const { +#if SANITIZER_NETBSD + return sizeof(struct reg) / sizeof(uptr); +#else return sizeof(regs_struct) / sizeof(uptr); +#endif } } // namespace __sanitizer -#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) - // || defined(__aarch64__) || defined(__powerpc64__) - // || defined(__s390__) || defined(__i386__) || defined(__arm__) +#endif Index: test/lsan/lit.common.cfg.py =================================================================== --- test/lsan/lit.common.cfg.py +++ test/lsan/lit.common.cfg.py @@ -70,7 +70,8 @@ # LeakSanitizer tests are currently supported on x86-64 Linux, PowerPC64 Linux, arm Linux, mips64 Linux, and x86_64 Darwin. supported_linux = config.host_os is 'Linux' and config.host_arch in ['x86_64', 'ppc64', 'ppc64le', 'mips64', 'arm', 'armhf', 'armv7l'] supported_darwin = config.host_os is 'Darwin' and config.target_arch is 'x86_64' -if not (supported_linux or supported_darwin): +supported_netbsd = config.host_os is 'NetBSD' and config.target_arch is 'x86_64' +if not (supported_linux or supported_darwin or supported_netbsd): config.unsupported = True # Don't support Thumb due to broken fast unwinder