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