diff options
8 files changed, 296 insertions, 14 deletions
diff --git a/compiler-rt/lib/interception/interception.h b/compiler-rt/lib/interception/interception.h index b6be72c6902..2ccc90387b2 100644 --- a/compiler-rt/lib/interception/interception.h +++ b/compiler-rt/lib/interception/interception.h @@ -23,20 +23,12 @@ // These typedefs should be used only in the interceptor definitions to replace // the standard system types (e.g. SSIZE_T instead of ssize_t) -typedef __sanitizer::uptr SIZE_T; -typedef __sanitizer::sptr SSIZE_T; -typedef __sanitizer::sptr PTRDIFF_T; -typedef __sanitizer::s64 INTMAX_T; -// WARNING: OFF_T may be different from OS type off_t, depending on the value of -// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls -// like pread and mmap, as opposed to pread64 and mmap64. -// Mac and Linux/x86-64 are special. -#if defined(__APPLE__) || (defined(__linux__) && defined(__x86_64__)) -typedef __sanitizer::u64 OFF_T; -#else -typedef __sanitizer::uptr OFF_T; -#endif -typedef __sanitizer::u64 OFF64_T; +typedef __sanitizer::uptr SIZE_T; +typedef __sanitizer::sptr SSIZE_T; +typedef __sanitizer::sptr PTRDIFF_T; +typedef __sanitizer::s64 INTMAX_T; +typedef __sanitizer::OFF_T OFF_T; +typedef __sanitizer::OFF64_T OFF64_T; // How to add an interceptor: // Suppose you need to wrap/replace system function (generally, from libc): diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt index 56aa3f730c9..b7628551be8 100644 --- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -36,6 +36,7 @@ set(SANITIZER_HEADERS sanitizer_internal_defs.h sanitizer_lfstack.h sanitizer_libc.h + sanitizer_linux.h sanitizer_list.h sanitizer_mutex.h sanitizer_placement_new.h diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h index 083761bbc71..e052cbdccb6 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -66,6 +66,16 @@ typedef signed int s32; typedef signed long long s64; // NOLINT typedef int fd_t; +// WARNING: OFF_T may be different from OS type off_t, depending on the value of +// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls +// like pread and mmap, as opposed to pread64 and mmap64. +// Mac and Linux/x86-64 are special. +#if defined(__APPLE__) || (defined(__linux__) && defined(__x86_64__)) +typedef u64 OFF_T; +#else +typedef uptr OFF_T; +#endif +typedef u64 OFF64_T; } // namespace __sanitizer extern "C" { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_libc.h b/compiler-rt/lib/sanitizer_common/sanitizer_libc.h index d4e954c263c..7c2a1b8574f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_libc.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_libc.h @@ -80,6 +80,11 @@ int internal_fstat(fd_t fd, void *buf); int internal_dup2(int oldfd, int newfd); uptr internal_readlink(const char *path, char *buf, uptr bufsize); void NORETURN internal__exit(int exitcode); +OFF_T internal_lseek(fd_t fd, OFF_T offset, int whence); + +long internal_ptrace(int request, int pid, void *addr, void *data); +int internal_waitpid(int pid, int *status, int options); +int internal_getppid(); // Threading int internal_sched_yield(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index 11cec22f998..6e29437f978 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -16,6 +16,7 @@ #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_linux.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" @@ -25,7 +26,9 @@ #include <pthread.h> #include <sched.h> #include <sys/mman.h> +#include <sys/ptrace.h> #include <sys/resource.h> +#include <sys/signal.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/time.h> @@ -539,6 +542,114 @@ void BlockingMutex::Unlock() { syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0); } +// ----------------- sanitizer_linux.h +// The actual size of this structure is specified by d_reclen. +// Note that getdents64 uses a different structure format. We only provide the +// 32-bit syscall here. +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +// Syscall wrappers. +long internal_ptrace(int request, int pid, void *addr, void *data) { + return syscall(__NR_ptrace, request, pid, addr, data); +} + +int internal_waitpid(int pid, int *status, int options) { + return syscall(__NR_wait4, pid, status, options, NULL /* rusage */); +} + +int internal_getppid() { + return syscall(__NR_getppid); +} + +int internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { + return syscall(__NR_getdents, fd, dirp, count); +} + +OFF_T internal_lseek(fd_t fd, OFF_T offset, int whence) { + return syscall(__NR_lseek, fd, offset, whence); +} + +int internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { + return syscall(__NR_prctl, option, arg2, arg3, arg4, arg5); +} + +int internal_sigaltstack(const struct sigaltstack *ss, + struct sigaltstack *oss) { + return syscall(__NR_sigaltstack, ss, oss); +} + + +// ThreadLister implementation. +ThreadLister::ThreadLister(int pid) + : pid_(pid), + descriptor_(-1), + error_(true), + entry_((linux_dirent *)buffer_), + bytes_read_(0) { + char task_directory_path[80]; + internal_snprintf(task_directory_path, sizeof(task_directory_path), + "/proc/%d/task/", pid); + descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); + if (descriptor_ < 0) { + error_ = true; + Report("Can't open /proc/%d/task for reading.\n", pid); + } else { + error_ = false; + } +} + +int ThreadLister::GetNextTID() { + int tid = -1; + do { + if (error_) + return -1; + if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries()) + return -1; + if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' && + entry_->d_name[0] <= '9') { + // Found a valid tid. + tid = (int)internal_atoll(entry_->d_name); + } + entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen); + } while (tid < 0); + return tid; +} + +void ThreadLister::Reset() { + if (error_ || descriptor_ < 0) + return; + internal_lseek(descriptor_, 0, SEEK_SET); +} + +ThreadLister::~ThreadLister() { + if (descriptor_ >= 0) + internal_close(descriptor_); +} + +bool ThreadLister::error() { return error_; } + +bool ThreadLister::GetDirectoryEntries() { + CHECK_GE(descriptor_, 0); + CHECK_NE(error_, true); + bytes_read_ = internal_getdents(descriptor_, + (struct linux_dirent *)buffer_, + sizeof(buffer_)); + if (bytes_read_ < 0) { + Report("Can't read directory entries from /proc/%d/task.\n", pid_); + error_ = true; + return false; + } else if (bytes_read_ == 0) { + return false; + } + entry_ = (struct linux_dirent *)buffer_; + return true; +} + } // namespace __sanitizer #endif // __linux__ diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h new file mode 100644 index 00000000000..b4ac3108256 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -0,0 +1,53 @@ +//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Linux-specific syscall wrappers and classes. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_H +#define SANITIZER_LINUX_H + +#include "sanitizer_internal_defs.h" + +struct sigaltstack; + +namespace __sanitizer { +// Dirent structure for getdents(). Note that this structure is different from +// the one in <dirent.h>, which is used by readdir(). +struct linux_dirent; + +// Syscall wrappers. +int internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); +int internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); +int internal_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss); + +// This class reads thread IDs from /proc/<pid>/task using only syscalls. +class ThreadLister { + public: + explicit ThreadLister(int pid); + ~ThreadLister(); + // GetNextTID returns -1 if the list of threads is exhausted, or if there has + // been an error. + int GetNextTID(); + void Reset(); + bool error(); + + private: + bool GetDirectoryEntries(); + + int pid_; + int descriptor_; + char buffer_[4096]; + bool error_; + struct linux_dirent* entry_; + int bytes_read_; +}; +} // namespace __sanitizer + +#endif // SANITIZER_LINUX_H diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt index 111dfeefdba..5b414b2f052 100644 --- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt @@ -5,6 +5,7 @@ set(SANITIZER_UNITTESTS sanitizer_common_test.cc sanitizer_flags_test.cc sanitizer_libc_test.cc + sanitizer_linux_test.cc sanitizer_list_test.cc sanitizer_mutex_test.cc sanitizer_printf_test.cc diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc new file mode 100644 index 00000000000..953b5c7ae9f --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc @@ -0,0 +1,109 @@ +//===-- sanitizer_linux_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_linux.h +// +//===----------------------------------------------------------------------===// + +#ifdef __linux__ + +#include "sanitizer_common/sanitizer_linux.h" +#include "gtest/gtest.h" + +#include "sanitizer_common/sanitizer_common.h" + +#include <pthread.h> +#include <sched.h> + +#include <set> + +namespace __sanitizer { +// In a single-threaded process, ThreadLister should produce the TID (which +// coincides with the PID) of the current task. +TEST(SanitizerLinux, ThreadListerSingleThread) { + pid_t pid = getpid(); + ThreadLister thread_lister(pid); + EXPECT_FALSE(thread_lister.error()); + EXPECT_EQ(thread_lister.GetNextTID(), pid); + EXPECT_FALSE(thread_lister.error()); + EXPECT_LT(thread_lister.GetNextTID(), 0); + EXPECT_FALSE(thread_lister.error()); +} + +static pthread_cond_t thread_exit_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread_exit_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t tid_reported_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t tid_reported_mutex = PTHREAD_MUTEX_INITIALIZER; +static bool thread_exit; + +void *TIDReporterThread(void *tid_storage) { + pthread_mutex_lock(&tid_reported_mutex); + *(pid_t *)tid_storage = GetTid(); + pthread_cond_broadcast(&tid_reported_cond); + pthread_mutex_unlock(&tid_reported_mutex); + + pthread_mutex_lock(&thread_exit_mutex); + while (!thread_exit) + pthread_cond_wait(&thread_exit_cond, &thread_exit_mutex); + pthread_mutex_unlock(&thread_exit_mutex); + return NULL; +} + +// In a process with multiple threads, ThreadLister should produce their TIDs +// in some order. +// Calling ThreadLister::Reset() should not change this. +TEST(SanitizerLinux, ThreadListerMultiThreaded) { + const uptr kThreadCount = 20; // does not include the main thread + pthread_t thread_ids[kThreadCount]; + pid_t thread_tids[kThreadCount]; + pid_t pid = getpid(); + pid_t self_tid = GetTid(); + thread_exit = false; + pthread_mutex_lock(&tid_reported_mutex); + for (uptr i = 0; i < kThreadCount; i++) { + int pthread_create_result; + thread_tids[i] = -1; + pthread_create_result = pthread_create(&thread_ids[i], NULL, + TIDReporterThread, + &thread_tids[i]); + ASSERT_EQ(pthread_create_result, 0); + while (thread_tids[i] == -1) + pthread_cond_wait(&tid_reported_cond, &tid_reported_mutex); + } + pthread_mutex_unlock(&tid_reported_mutex); + std::set<pid_t> reported_tids(thread_tids, thread_tids + kThreadCount); + reported_tids.insert(self_tid); + + ThreadLister thread_lister(pid); + // There's a Reset() call between the first and second iteration. + for (uptr i = 0; i < 2; i++) { + std::set<pid_t> listed_tids; + + EXPECT_FALSE(thread_lister.error()); + for (uptr i = 0; i < kThreadCount + 1; i++) { + pid_t tid = thread_lister.GetNextTID(); + EXPECT_GE(tid, 0); + EXPECT_FALSE(thread_lister.error()); + listed_tids.insert(tid); + } + pid_t tid = thread_lister.GetNextTID(); + EXPECT_LT(tid, 0); + EXPECT_FALSE(thread_lister.error()); + + EXPECT_EQ(listed_tids, reported_tids); + thread_lister.Reset(); + } + pthread_mutex_lock(&thread_exit_mutex); + thread_exit = true; + pthread_cond_broadcast(&thread_exit_cond); + pthread_mutex_unlock(&thread_exit_mutex); +} +} // namespace __sanitizer + +#endif // __linux__ |