LCOV - code coverage report
Current view: top level - toolkit/crashreporter/breakpad-client/linux/minidump_writer - linux_ptrace_dumper.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 122 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012, Google Inc.
       2             : // All rights reserved.
       3             : //
       4             : // Redistribution and use in source and binary forms, with or without
       5             : // modification, are permitted provided that the following conditions are
       6             : // met:
       7             : //
       8             : //     * Redistributions of source code must retain the above copyright
       9             : // notice, this list of conditions and the following disclaimer.
      10             : //     * Redistributions in binary form must reproduce the above
      11             : // copyright notice, this list of conditions and the following disclaimer
      12             : // in the documentation and/or other materials provided with the
      13             : // distribution.
      14             : //     * Neither the name of Google Inc. nor the names of its
      15             : // contributors may be used to endorse or promote products derived from
      16             : // this software without specific prior written permission.
      17             : //
      18             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      22             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      23             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      24             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      28             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29             : 
      30             : // linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
      31             : // See linux_ptrace_dumper.h for detals.
      32             : // This class was originally splitted from google_breakpad::LinuxDumper.
      33             : 
      34             : // This code deals with the mechanics of getting information about a crashed
      35             : // process. Since this code may run in a compromised address space, the same
      36             : // rules apply as detailed at the top of minidump_writer.h: no libc calls and
      37             : // use the alternative allocator.
      38             : 
      39             : #include "linux/minidump_writer/linux_ptrace_dumper.h"
      40             : 
      41             : #include <asm/ptrace.h>
      42             : #include <assert.h>
      43             : #include <errno.h>
      44             : #include <fcntl.h>
      45             : #include <limits.h>
      46             : #include <stddef.h>
      47             : #include <stdlib.h>
      48             : #include <string.h>
      49             : #include <sys/ptrace.h>
      50             : #include <sys/uio.h>
      51             : #include <sys/wait.h>
      52             : 
      53             : #if defined(__i386)
      54             : #include <cpuid.h>
      55             : #endif
      56             : 
      57             : #include "linux/minidump_writer/directory_reader.h"
      58             : #include "linux/minidump_writer/line_reader.h"
      59             : #include "common/linux/linux_libc_support.h"
      60             : #include "third_party/lss/linux_syscall_support.h"
      61             : 
      62             : // Suspends a thread by attaching to it.
      63           0 : static bool SuspendThread(pid_t pid) {
      64             :   // This may fail if the thread has just died or debugged.
      65           0 :   errno = 0;
      66           0 :   if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
      67           0 :       errno != 0) {
      68           0 :     return false;
      69             :   }
      70           0 :   while (sys_waitpid(pid, NULL, __WALL) < 0) {
      71           0 :     if (errno != EINTR) {
      72           0 :       sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
      73           0 :       return false;
      74             :     }
      75             :   }
      76             : #if defined(__i386) || defined(__x86_64)
      77             :   // On x86, the stack pointer is NULL or -1, when executing trusted code in
      78             :   // the seccomp sandbox. Not only does this cause difficulties down the line
      79             :   // when trying to dump the thread's stack, it also results in the minidumps
      80             :   // containing information about the trusted threads. This information is
      81             :   // generally completely meaningless and just pollutes the minidumps.
      82             :   // We thus test the stack pointer and exclude any threads that are part of
      83             :   // the seccomp sandbox's trusted code.
      84             :   user_regs_struct regs;
      85           0 :   if (sys_ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1 ||
      86             : #if defined(__i386)
      87             :       !regs.esp
      88             : #elif defined(__x86_64)
      89           0 :       !regs.rsp
      90             : #endif
      91             :       ) {
      92           0 :     sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
      93           0 :     return false;
      94             :   }
      95             : #endif
      96           0 :   return true;
      97             : }
      98             : 
      99             : // Resumes a thread by detaching from it.
     100           0 : static bool ResumeThread(pid_t pid) {
     101           0 :   return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
     102             : }
     103             : 
     104             : namespace google_breakpad {
     105             : 
     106           0 : LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
     107             :     : LinuxDumper(pid),
     108           0 :       threads_suspended_(false) {
     109           0 : }
     110             : 
     111           0 : bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
     112             :                                       const char* node) const {
     113           0 :   if (!path || !node || pid <= 0)
     114           0 :     return false;
     115             : 
     116           0 :   size_t node_len = my_strlen(node);
     117           0 :   if (node_len == 0)
     118           0 :     return false;
     119             : 
     120           0 :   const unsigned pid_len = my_uint_len(pid);
     121           0 :   const size_t total_length = 6 + pid_len + 1 + node_len;
     122           0 :   if (total_length >= NAME_MAX)
     123           0 :     return false;
     124             : 
     125           0 :   my_memcpy(path, "/proc/", 6);
     126           0 :   my_uitos(path + 6, pid, pid_len);
     127           0 :   path[6 + pid_len] = '/';
     128           0 :   my_memcpy(path + 6 + pid_len + 1, node, node_len);
     129           0 :   path[total_length] = '\0';
     130           0 :   return true;
     131             : }
     132             : 
     133           0 : bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
     134             :                                         const void* src, size_t length) {
     135           0 :   unsigned long tmp = 55;
     136           0 :   size_t done = 0;
     137             :   static const size_t word_size = sizeof(tmp);
     138           0 :   uint8_t* const local = (uint8_t*) dest;
     139           0 :   uint8_t* const remote = (uint8_t*) src;
     140             : 
     141           0 :   while (done < length) {
     142           0 :     const size_t l = (length - done > word_size) ? word_size : (length - done);
     143           0 :     if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
     144           0 :       tmp = 0;
     145             :     }
     146           0 :     my_memcpy(local + done, &tmp, l);
     147           0 :     done += l;
     148             :   }
     149           0 :   return true;
     150             : }
     151             : 
     152             : // Read thread info from /proc/$pid/status.
     153             : // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
     154             : // these members are set to -1. Returns true iff all three members are
     155             : // available.
     156           0 : bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
     157           0 :   if (index >= threads_.size())
     158           0 :     return false;
     159             : 
     160           0 :   pid_t tid = threads_[index];
     161             : 
     162           0 :   assert(info != NULL);
     163             :   char status_path[NAME_MAX];
     164           0 :   if (!BuildProcPath(status_path, tid, "status"))
     165           0 :     return false;
     166             : 
     167           0 :   const int fd = sys_open(status_path, O_RDONLY, 0);
     168           0 :   if (fd < 0)
     169           0 :     return false;
     170             : 
     171           0 :   LineReader* const line_reader = new(allocator_) LineReader(fd);
     172             :   const char* line;
     173             :   unsigned line_len;
     174             : 
     175           0 :   info->ppid = info->tgid = -1;
     176             : 
     177           0 :   while (line_reader->GetNextLine(&line, &line_len)) {
     178           0 :     if (my_strncmp("Tgid:\t", line, 6) == 0) {
     179           0 :       my_strtoui(&info->tgid, line + 6);
     180           0 :     } else if (my_strncmp("PPid:\t", line, 6) == 0) {
     181           0 :       my_strtoui(&info->ppid, line + 6);
     182             :     }
     183             : 
     184           0 :     line_reader->PopLine(line_len);
     185             :   }
     186           0 :   sys_close(fd);
     187             : 
     188           0 :   if (info->ppid == -1 || info->tgid == -1)
     189           0 :     return false;
     190             : 
     191             : #ifdef PTRACE_GETREGSET
     192             :   struct iovec io;
     193           0 :   info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
     194           0 :   if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
     195           0 :     return false;
     196             :   }
     197             : 
     198           0 :   info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
     199           0 :   if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
     200           0 :     return false;
     201             :   }
     202             : #else  // PTRACE_GETREGSET
     203             :   void* gp_addr;
     204             :   info->GetGeneralPurposeRegisters(&gp_addr, NULL);
     205             :   if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
     206             :     return false;
     207             :   }
     208             : 
     209             : #if !(defined(__ANDROID__) && defined(__ARM_EABI__))
     210             :   // When running an arm build on an arm64 device, attempting to get the
     211             :   // floating point registers fails. On Android, the floating point registers
     212             :   // aren't written to the cpu context anyway, so just don't get them here.
     213             :   // See http://crbug.com/508324
     214             :   void* fp_addr;
     215             :   info->GetFloatingPointRegisters(&fp_addr, NULL);
     216             :   if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
     217             :     return false;
     218             :   }
     219             : #endif
     220             : #endif  // PTRACE_GETREGSET
     221             : 
     222             : #if defined(__i386)
     223             : #if !defined(bit_FXSAVE)  // e.g. Clang
     224             : #define bit_FXSAVE bit_FXSR
     225             : #endif
     226             :   // Detect if the CPU supports the FXSAVE/FXRSTOR instructions
     227             :   int eax, ebx, ecx, edx;
     228             :   __cpuid(1, eax, ebx, ecx, edx);
     229             :   if (edx & bit_FXSAVE) {
     230             :     if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
     231             :       return false;
     232             :     }
     233             :   } else {
     234             :     memset(&info->fpxregs, 0, sizeof(info->fpxregs));
     235             :   }
     236             : #endif  // defined(__i386)
     237             : 
     238             : #if defined(__i386) || defined(__x86_64)
     239           0 :   for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
     240           0 :     if (sys_ptrace(
     241             :         PTRACE_PEEKUSER, tid,
     242             :         reinterpret_cast<void*> (offsetof(struct user,
     243             :                                           u_debugreg[0]) + i *
     244             :                                  sizeof(debugreg_t)),
     245           0 :         &info->dregs[i]) == -1) {
     246           0 :       return false;
     247             :     }
     248             :   }
     249             : #endif
     250             : 
     251             : #if defined(__mips__)
     252             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     253             :              reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
     254             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     255             :              reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1);
     256             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     257             :              reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2);
     258             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     259             :              reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2);
     260             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     261             :              reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3);
     262             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     263             :              reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3);
     264             :   sys_ptrace(PTRACE_PEEKUSER, tid,
     265             :              reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
     266             : #endif
     267             : 
     268             :   const uint8_t* stack_pointer;
     269             : #if defined(__i386)
     270             :   my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
     271             : #elif defined(__x86_64)
     272           0 :   my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
     273             : #elif defined(__ARM_EABI__)
     274             :   my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
     275             : #elif defined(__aarch64__)
     276             :   my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
     277             : #elif defined(__mips__)
     278             :   stack_pointer =
     279             :       reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
     280             : #else
     281             : #error "This code hasn't been ported to your platform yet."
     282             : #endif
     283           0 :   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
     284             : 
     285           0 :   return true;
     286             : }
     287             : 
     288           0 : bool LinuxPtraceDumper::IsPostMortem() const {
     289           0 :   return false;
     290             : }
     291             : 
     292           0 : bool LinuxPtraceDumper::ThreadsSuspend() {
     293           0 :   if (threads_suspended_)
     294           0 :     return true;
     295           0 :   for (size_t i = 0; i < threads_.size(); ++i) {
     296           0 :     if (!SuspendThread(threads_[i])) {
     297             :       // If the thread either disappeared before we could attach to it, or if
     298             :       // it was part of the seccomp sandbox's trusted code, it is OK to
     299             :       // silently drop it from the minidump.
     300           0 :       if (i < threads_.size() - 1) {
     301           0 :         my_memmove(&threads_[i], &threads_[i + 1],
     302           0 :                    (threads_.size() - i - 1) * sizeof(threads_[i]));
     303             :       }
     304           0 :       threads_.resize(threads_.size() - 1);
     305           0 :       --i;
     306             :     }
     307             :   }
     308           0 :   threads_suspended_ = true;
     309           0 :   return threads_.size() > 0;
     310             : }
     311             : 
     312           0 : bool LinuxPtraceDumper::ThreadsResume() {
     313           0 :   if (!threads_suspended_)
     314           0 :     return false;
     315           0 :   bool good = true;
     316           0 :   for (size_t i = 0; i < threads_.size(); ++i)
     317           0 :     good &= ResumeThread(threads_[i]);
     318           0 :   threads_suspended_ = false;
     319           0 :   return good;
     320             : }
     321             : 
     322             : // Parse /proc/$pid/task to list all the threads of the process identified by
     323             : // pid.
     324           0 : bool LinuxPtraceDumper::EnumerateThreads() {
     325             :   char task_path[NAME_MAX];
     326           0 :   if (!BuildProcPath(task_path, pid_, "task"))
     327           0 :     return false;
     328             : 
     329           0 :   const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
     330           0 :   if (fd < 0)
     331           0 :     return false;
     332           0 :   DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
     333             : 
     334             :   // The directory may contain duplicate entries which we filter by assuming
     335             :   // that they are consecutive.
     336           0 :   int last_tid = -1;
     337             :   const char* dent_name;
     338           0 :   while (dir_reader->GetNextEntry(&dent_name)) {
     339           0 :     if (my_strcmp(dent_name, ".") &&
     340           0 :         my_strcmp(dent_name, "..")) {
     341           0 :       int tid = 0;
     342           0 :       if (my_strtoui(&tid, dent_name) &&
     343           0 :           last_tid != tid) {
     344           0 :         last_tid = tid;
     345           0 :         threads_.push_back(tid);
     346             :       }
     347             :     }
     348           0 :     dir_reader->PopEntry();
     349             :   }
     350             : 
     351           0 :   sys_close(fd);
     352           0 :   return true;
     353             : }
     354             : 
     355             : }  // namespace google_breakpad

Generated by: LCOV version 1.13