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. 63 static 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. 100 static bool ResumeThread(pid_t pid) { 101 return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; 102 } 103 104 namespace google_breakpad { 105 106 LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) 107 : LinuxDumper(pid), 108 threads_suspended_(false) { 109 } 110 111 bool 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 133 bool 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. 156 bool 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 274 bool LinuxPtraceDumper::IsPostMortem() const { 275 return false; 276 } 277 278 bool 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 298 bool 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. 310 bool 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