Home | History | Annotate | Download | only in libmemunreachable
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "ThreadCapture.h"
     18 
     19 #include <elf.h>
     20 #include <errno.h>
     21 #include <fcntl.h>
     22 #include <limits.h>
     23 #include <stdlib.h>
     24 #include <sys/ptrace.h>
     25 #include <sys/stat.h>
     26 #include <sys/syscall.h>
     27 #include <sys/types.h>
     28 #include <sys/uio.h>
     29 #include <sys/wait.h>
     30 #include <unistd.h>
     31 
     32 #include <map>
     33 #include <memory>
     34 #include <set>
     35 #include <vector>
     36 
     37 #include <android-base/unique_fd.h>
     38 
     39 #include "Allocator.h"
     40 #include "log.h"
     41 
     42 namespace android {
     43 
     44 // bionic interfaces used:
     45 // atoi
     46 // strlcat
     47 // writev
     48 
     49 // bionic interfaces reimplemented to avoid allocation:
     50 // getdents64
     51 
     52 // Convert a pid > 0 to a string.  sprintf might allocate, so we can't use it.
     53 // Returns a pointer somewhere in buf to a null terminated string, or NULL
     54 // on error.
     55 static char* pid_to_str(char* buf, size_t len, pid_t pid) {
     56   if (pid <= 0) {
     57     return nullptr;
     58   }
     59 
     60   char* ptr = buf + len - 1;
     61   *ptr = 0;
     62   while (pid > 0) {
     63     ptr--;
     64     if (ptr < buf) {
     65       return nullptr;
     66     }
     67     *ptr = '0' + (pid % 10);
     68     pid /= 10;
     69   }
     70 
     71   return ptr;
     72 }
     73 
     74 class ThreadCaptureImpl {
     75  public:
     76   ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
     77   ~ThreadCaptureImpl() {}
     78   bool ListThreads(TidList& tids);
     79   bool CaptureThreads();
     80   bool ReleaseThreads();
     81   bool ReleaseThread(pid_t tid);
     82   bool CapturedThreadInfo(ThreadInfoList& threads);
     83   void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
     84 
     85  private:
     86   int CaptureThread(pid_t tid);
     87   bool ReleaseThread(pid_t tid, unsigned int signal);
     88   int PtraceAttach(pid_t tid);
     89   void PtraceDetach(pid_t tid, unsigned int signal);
     90   bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
     91 
     92   allocator::map<pid_t, unsigned int> captured_threads_;
     93   Allocator<ThreadCaptureImpl> allocator_;
     94   pid_t pid_;
     95   std::function<void(pid_t)> inject_test_func_;
     96 };
     97 
     98 ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
     99     : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
    100 
    101 bool ThreadCaptureImpl::ListThreads(TidList& tids) {
    102   tids.clear();
    103 
    104   char pid_buf[11];
    105   char path[256] = "/proc/";
    106   char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
    107   if (!pid_str) {
    108     return false;
    109   }
    110   strlcat(path, pid_str, sizeof(path));
    111   strlcat(path, "/task", sizeof(path));
    112 
    113   android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
    114   if (fd == -1) {
    115     MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
    116     return false;
    117   }
    118 
    119   struct linux_dirent64 {
    120     uint64_t d_ino;
    121     int64_t d_off;
    122     uint16_t d_reclen;
    123     char d_type;
    124     char d_name[];
    125   } __attribute((packed));
    126   char dirent_buf[4096];
    127   ssize_t nread;
    128   do {
    129     nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
    130     if (nread < 0) {
    131       MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
    132       return false;
    133     } else if (nread > 0) {
    134       ssize_t off = 0;
    135       while (off < nread) {
    136         linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
    137         off += dirent->d_reclen;
    138         pid_t tid = atoi(dirent->d_name);
    139         if (tid <= 0) {
    140           continue;
    141         }
    142         tids.push_back(tid);
    143       }
    144     }
    145 
    146   } while (nread != 0);
    147 
    148   return true;
    149 }
    150 
    151 bool ThreadCaptureImpl::CaptureThreads() {
    152   TidList tids{allocator_};
    153 
    154   bool found_new_thread;
    155   do {
    156     if (!ListThreads(tids)) {
    157       ReleaseThreads();
    158       return false;
    159     }
    160 
    161     found_new_thread = false;
    162 
    163     for (auto it = tids.begin(); it != tids.end(); it++) {
    164       auto captured = captured_threads_.find(*it);
    165       if (captured == captured_threads_.end()) {
    166         if (CaptureThread(*it) < 0) {
    167           ReleaseThreads();
    168           return false;
    169         }
    170         found_new_thread = true;
    171       }
    172     }
    173   } while (found_new_thread);
    174 
    175   return true;
    176 }
    177 
    178 // Detatches from a thread, delivering signal if nonzero, logs on error
    179 void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
    180   void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
    181   if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
    182     MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
    183   }
    184 }
    185 
    186 // Attaches to and pauses thread.
    187 // Returns 1 on attach, 0 on tid not found, -1 and logs on error
    188 int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
    189   int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
    190   if (ret < 0) {
    191     MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
    192     return -1;
    193   }
    194 
    195   if (inject_test_func_) {
    196     inject_test_func_(tid);
    197   }
    198 
    199   if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
    200     if (errno == ESRCH) {
    201       return 0;
    202     } else {
    203       MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
    204       PtraceDetach(tid, 0);
    205       return -1;
    206     }
    207   }
    208   return 1;
    209 }
    210 
    211 bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
    212   thread_info.tid = tid;
    213 
    214   const unsigned int max_num_regs = 128;  // larger than number of registers on any device
    215   uintptr_t regs[max_num_regs];
    216   struct iovec iovec;
    217   iovec.iov_base = &regs;
    218   iovec.iov_len = sizeof(regs);
    219 
    220   if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
    221     MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
    222     return false;
    223   }
    224 
    225   unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
    226   thread_info.regs.assign(&regs[0], &regs[num_regs]);
    227 
    228   const int sp =
    229 #if defined(__x86_64__)
    230       offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
    231 #elif defined(__i386__)
    232       offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
    233 #elif defined(__arm__)
    234       offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
    235 #elif defined(__aarch64__)
    236       offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
    237 #elif defined(__mips__) || defined(__mips64__)
    238       offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
    239 #else
    240 #error Unrecognized architecture
    241 #endif
    242       ;
    243 
    244   // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
    245 
    246   thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
    247 
    248   return true;
    249 }
    250 
    251 int ThreadCaptureImpl::CaptureThread(pid_t tid) {
    252   int ret = PtraceAttach(tid);
    253   if (ret <= 0) {
    254     return ret;
    255   }
    256 
    257   int status = 0;
    258   if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
    259     MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
    260     PtraceDetach(tid, 0);
    261     return -1;
    262   }
    263 
    264   if (!WIFSTOPPED(status)) {
    265     MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
    266     return 0;
    267   }
    268 
    269   unsigned int resume_signal = 0;
    270 
    271   unsigned int signal = WSTOPSIG(status);
    272   if ((status >> 16) == PTRACE_EVENT_STOP) {
    273     switch (signal) {
    274       case SIGSTOP:
    275       case SIGTSTP:
    276       case SIGTTIN:
    277       case SIGTTOU:
    278         // group-stop signals
    279         break;
    280       case SIGTRAP:
    281         // normal ptrace interrupt stop
    282         break;
    283       default:
    284         MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
    285                   tid, pid_);
    286         return -1;
    287     }
    288   } else {
    289     // signal-delivery-stop
    290     resume_signal = signal;
    291   }
    292 
    293   captured_threads_[tid] = resume_signal;
    294   return 1;
    295 }
    296 
    297 bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
    298   auto it = captured_threads_.find(tid);
    299   if (it == captured_threads_.end()) {
    300     return false;
    301   }
    302   return ReleaseThread(it->first, it->second);
    303 }
    304 
    305 bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
    306   PtraceDetach(tid, signal);
    307   return true;
    308 }
    309 
    310 bool ThreadCaptureImpl::ReleaseThreads() {
    311   bool ret = true;
    312   for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
    313     if (ReleaseThread(it->first, it->second)) {
    314       it = captured_threads_.erase(it);
    315     } else {
    316       it++;
    317       ret = false;
    318     }
    319   }
    320   return ret;
    321 }
    322 
    323 bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
    324   threads.clear();
    325 
    326   for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
    327     ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
    328     if (!PtraceThreadInfo(it->first, t)) {
    329       return false;
    330     }
    331     threads.push_back(t);
    332   }
    333   return true;
    334 }
    335 
    336 ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
    337   Allocator<ThreadCaptureImpl> impl_allocator = allocator;
    338   impl_ = impl_allocator.make_unique(pid, impl_allocator);
    339 }
    340 
    341 ThreadCapture::~ThreadCapture() {}
    342 
    343 bool ThreadCapture::ListThreads(TidList& tids) {
    344   return impl_->ListThreads(tids);
    345 }
    346 
    347 bool ThreadCapture::CaptureThreads() {
    348   return impl_->CaptureThreads();
    349 }
    350 
    351 bool ThreadCapture::ReleaseThreads() {
    352   return impl_->ReleaseThreads();
    353 }
    354 
    355 bool ThreadCapture::ReleaseThread(pid_t tid) {
    356   return impl_->ReleaseThread(tid);
    357 }
    358 
    359 bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
    360   return impl_->CapturedThreadInfo(threads);
    361 }
    362 
    363 void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
    364   impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
    365 }
    366 
    367 }  // namespace android
    368