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 "client/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 "client/linux/minidump_writer/directory_reader.h" 58#include "client/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. 63static bool SuspendThread(pid_t pid) { 64 // This may fail if the thread has just died or debugged. 65 errno = 0; 66 if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && 67 errno != 0) { 68 return false; 69 } 70 while (sys_waitpid(pid, NULL, __WALL) < 0) { 71 if (errno != EINTR) { 72 sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); 73 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 if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || 86#if defined(__i386) 87 !regs.esp 88#elif defined(__x86_64) 89 !regs.rsp 90#endif 91 ) { 92 sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); 93 return false; 94 } 95#endif 96 return true; 97} 98 99// Resumes a thread by detaching from it. 100static bool ResumeThread(pid_t pid) { 101 return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; 102} 103 104namespace google_breakpad { 105 106LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) 107 : LinuxDumper(pid), 108 threads_suspended_(false) { 109} 110 111bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, 112 const char* node) const { 113 if (!path || !node || pid <= 0) 114 return false; 115 116 size_t node_len = my_strlen(node); 117 if (node_len == 0) 118 return false; 119 120 const unsigned pid_len = my_uint_len(pid); 121 const size_t total_length = 6 + pid_len + 1 + node_len; 122 if (total_length >= NAME_MAX) 123 return false; 124 125 my_memcpy(path, "/proc/", 6); 126 my_uitos(path + 6, pid, pid_len); 127 path[6 + pid_len] = '/'; 128 my_memcpy(path + 6 + pid_len + 1, node, node_len); 129 path[total_length] = '\0'; 130 return true; 131} 132 133bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, 134 const void* src, size_t length) { 135 unsigned long tmp = 55; 136 size_t done = 0; 137 static const size_t word_size = sizeof(tmp); 138 uint8_t* const local = (uint8_t*) dest; 139 uint8_t* const remote = (uint8_t*) src; 140 141 while (done < length) { 142 const size_t l = (length - done > word_size) ? word_size : (length - done); 143 if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { 144 tmp = 0; 145 } 146 my_memcpy(local + done, &tmp, l); 147 done += l; 148 } 149 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. 156bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { 157 if (index >= threads_.size()) 158 return false; 159 160 pid_t tid = threads_[index]; 161 162 assert(info != NULL); 163 char status_path[NAME_MAX]; 164 if (!BuildProcPath(status_path, tid, "status")) 165 return false; 166 167 const int fd = sys_open(status_path, O_RDONLY, 0); 168 if (fd < 0) 169 return false; 170 171 LineReader* const line_reader = new(allocator_) LineReader(fd); 172 const char* line; 173 unsigned line_len; 174 175 info->ppid = info->tgid = -1; 176 177 while (line_reader->GetNextLine(&line, &line_len)) { 178 if (my_strncmp("Tgid:\t", line, 6) == 0) { 179 my_strtoui(&info->tgid, line + 6); 180 } else if (my_strncmp("PPid:\t", line, 6) == 0) { 181 my_strtoui(&info->ppid, line + 6); 182 } 183 184 line_reader->PopLine(line_len); 185 } 186 sys_close(fd); 187 188 if (info->ppid == -1 || info->tgid == -1) 189 return false; 190 191#ifdef PTRACE_GETREGSET 192 struct iovec io; 193 io.iov_base = &info->regs; 194 io.iov_len = sizeof(info->regs); 195 if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { 196 return false; 197 } 198 199 io.iov_base = &info->fpregs; 200 io.iov_len = sizeof(info->fpregs); 201 if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { 202 return false; 203 } 204#else 205 if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { 206 return false; 207 } 208 209 if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { 210 return false; 211 } 212#endif 213 214#if defined(__i386) 215#if !defined(bit_FXSAVE) // e.g. Clang 216#define bit_FXSAVE bit_FXSR 217#endif 218 // Detect if the CPU supports the FXSAVE/FXRSTOR instructions 219 int eax, ebx, ecx, edx; 220 __cpuid(1, eax, ebx, ecx, edx); 221 if (edx & bit_FXSAVE) { 222 if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) { 223 return false; 224 } 225 } else { 226 memset(&info->fpxregs, 0, sizeof(info->fpxregs)); 227 } 228#endif // defined(__i386) 229 230#if defined(__i386) || defined(__x86_64) 231 for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { 232 if (sys_ptrace( 233 PTRACE_PEEKUSER, tid, 234 reinterpret_cast<void*> (offsetof(struct user, 235 u_debugreg[0]) + i * 236 sizeof(debugreg_t)), 237 &info->dregs[i]) == -1) { 238 return false; 239 } 240 } 241#endif 242 243#if defined(__mips__) 244 for (int i = 0; i < 3; ++i) { 245 sys_ptrace(PTRACE_PEEKUSER, tid, 246 reinterpret_cast<void*>(DSP_BASE + (i * 2)), &info->hi[i]); 247 sys_ptrace(PTRACE_PEEKUSER, tid, 248 reinterpret_cast<void*>(DSP_BASE + (i * 2) + 1), &info->lo[i]); 249 } 250 sys_ptrace(PTRACE_PEEKUSER, tid, 251 reinterpret_cast<void*>(DSP_CONTROL), &info->dsp_control); 252#endif 253 254 const uint8_t* stack_pointer; 255#if defined(__i386) 256 my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); 257#elif defined(__x86_64) 258 my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); 259#elif defined(__ARM_EABI__) 260 my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); 261#elif defined(__aarch64__) 262 my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); 263#elif defined(__mips__) 264 stack_pointer = 265 reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]); 266#else 267#error "This code hasn't been ported to your platform yet." 268#endif 269 info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); 270 271 return true; 272} 273 274bool LinuxPtraceDumper::IsPostMortem() const { 275 return false; 276} 277 278bool LinuxPtraceDumper::ThreadsSuspend() { 279 if (threads_suspended_) 280 return true; 281 for (size_t i = 0; i < threads_.size(); ++i) { 282 if (!SuspendThread(threads_[i])) { 283 // If the thread either disappeared before we could attach to it, or if 284 // it was part of the seccomp sandbox's trusted code, it is OK to 285 // silently drop it from the minidump. 286 if (i < threads_.size() - 1) { 287 my_memmove(&threads_[i], &threads_[i + 1], 288 (threads_.size() - i - 1) * sizeof(threads_[i])); 289 } 290 threads_.resize(threads_.size() - 1); 291 --i; 292 } 293 } 294 threads_suspended_ = true; 295 return threads_.size() > 0; 296} 297 298bool LinuxPtraceDumper::ThreadsResume() { 299 if (!threads_suspended_) 300 return false; 301 bool good = true; 302 for (size_t i = 0; i < threads_.size(); ++i) 303 good &= ResumeThread(threads_[i]); 304 threads_suspended_ = false; 305 return good; 306} 307 308// Parse /proc/$pid/task to list all the threads of the process identified by 309// pid. 310bool LinuxPtraceDumper::EnumerateThreads() { 311 char task_path[NAME_MAX]; 312 if (!BuildProcPath(task_path, pid_, "task")) 313 return false; 314 315 const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); 316 if (fd < 0) 317 return false; 318 DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); 319 320 // The directory may contain duplicate entries which we filter by assuming 321 // that they are consecutive. 322 int last_tid = -1; 323 const char* dent_name; 324 while (dir_reader->GetNextEntry(&dent_name)) { 325 if (my_strcmp(dent_name, ".") && 326 my_strcmp(dent_name, "..")) { 327 int tid = 0; 328 if (my_strtoui(&tid, dent_name) && 329 last_tid != tid) { 330 last_tid = tid; 331 threads_.push_back(tid); 332 } 333 } 334 dir_reader->PopEntry(); 335 } 336 337 sys_close(fd); 338 return true; 339} 340 341} // namespace google_breakpad 342