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, ®s) == -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
|