summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler-rt/lib/interception/interception.h20
-rw-r--r--compiler-rt/lib/sanitizer_common/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h10
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_libc.h5
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_linux.cc111
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_linux.h53
-rw-r--r--compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc109
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__
OpenPOWER on IntegriCloud