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 | bool LinuxPtraceDumper::ReadRegisterSet(ThreadInfo* info, pid_t tid) |
153 | { |
154 | #ifdef PTRACE_GETREGSET |
155 | struct iovec io; |
156 | info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len); |
157 | if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { |
158 | return false; |
159 | } |
160 | |
161 | info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len); |
162 | if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { |
163 | return false; |
164 | } |
165 | return true; |
166 | #else |
167 | return false; |
168 | #endif |
169 | } |
170 | |
171 | bool LinuxPtraceDumper::ReadRegisters(ThreadInfo* info, pid_t tid) { |
172 | #ifdef PTRACE_GETREGS |
173 | void* gp_addr; |
174 | info->GetGeneralPurposeRegisters(&gp_addr, NULL); |
175 | if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) { |
176 | return false; |
177 | } |
178 | |
179 | #if !(defined(__ANDROID__) && defined(__ARM_EABI__)) |
180 | // When running an arm build on an arm64 device, attempting to get the |
181 | // floating point registers fails. On Android, the floating point registers |
182 | // aren't written to the cpu context anyway, so just don't get them here. |
183 | // See http://crbug.com/508324 |
184 | void* fp_addr; |
185 | info->GetFloatingPointRegisters(&fp_addr, NULL); |
186 | if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) { |
187 | return false; |
188 | } |
189 | #endif // !(defined(__ANDROID__) && defined(__ARM_EABI__)) |
190 | return true; |
191 | #else // PTRACE_GETREGS |
192 | return false; |
193 | #endif |
194 | } |
195 | |
196 | // Read thread info from /proc/$pid/status. |
197 | // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, |
198 | // these members are set to -1. Returns true iff all three members are |
199 | // available. |
200 | bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { |
201 | if (index >= threads_.size()) |
202 | return false; |
203 | |
204 | pid_t tid = threads_[index]; |
205 | |
206 | assert(info != NULL); |
207 | char status_path[NAME_MAX]; |
208 | if (!BuildProcPath(status_path, tid, "status" )) |
209 | return false; |
210 | |
211 | const int fd = sys_open(status_path, O_RDONLY, 0); |
212 | if (fd < 0) |
213 | return false; |
214 | |
215 | LineReader* const line_reader = new(allocator_) LineReader(fd); |
216 | const char* line; |
217 | unsigned line_len; |
218 | |
219 | info->ppid = info->tgid = -1; |
220 | |
221 | while (line_reader->GetNextLine(&line, &line_len)) { |
222 | if (my_strncmp("Tgid:\t" , line, 6) == 0) { |
223 | my_strtoui(&info->tgid, line + 6); |
224 | } else if (my_strncmp("PPid:\t" , line, 6) == 0) { |
225 | my_strtoui(&info->ppid, line + 6); |
226 | } |
227 | |
228 | line_reader->PopLine(line_len); |
229 | } |
230 | sys_close(fd); |
231 | |
232 | if (info->ppid == -1 || info->tgid == -1) |
233 | return false; |
234 | |
235 | if (!ReadRegisterSet(info, tid)) { |
236 | if (!ReadRegisters(info, tid)) { |
237 | return false; |
238 | } |
239 | } |
240 | |
241 | #if defined(__i386) |
242 | #if !defined(bit_FXSAVE) // e.g. Clang |
243 | #define bit_FXSAVE bit_FXSR |
244 | #endif |
245 | // Detect if the CPU supports the FXSAVE/FXRSTOR instructions |
246 | int eax, ebx, ecx, edx; |
247 | __cpuid(1, eax, ebx, ecx, edx); |
248 | if (edx & bit_FXSAVE) { |
249 | if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) { |
250 | return false; |
251 | } |
252 | } else { |
253 | memset(&info->fpxregs, 0, sizeof(info->fpxregs)); |
254 | } |
255 | #endif // defined(__i386) |
256 | |
257 | #if defined(__i386) || defined(__x86_64) |
258 | for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { |
259 | if (sys_ptrace( |
260 | PTRACE_PEEKUSER, tid, |
261 | reinterpret_cast<void*> (offsetof(struct user, |
262 | u_debugreg[0]) + i * |
263 | sizeof(debugreg_t)), |
264 | &info->dregs[i]) == -1) { |
265 | return false; |
266 | } |
267 | } |
268 | #endif |
269 | |
270 | #if defined(__mips__) |
271 | sys_ptrace(PTRACE_PEEKUSER, tid, |
272 | reinterpret_cast<void*>(PC), &info->mcontext.pc); |
273 | sys_ptrace(PTRACE_PEEKUSER, tid, |
274 | reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1); |
275 | sys_ptrace(PTRACE_PEEKUSER, tid, |
276 | reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1); |
277 | sys_ptrace(PTRACE_PEEKUSER, tid, |
278 | reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2); |
279 | sys_ptrace(PTRACE_PEEKUSER, tid, |
280 | reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2); |
281 | sys_ptrace(PTRACE_PEEKUSER, tid, |
282 | reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3); |
283 | sys_ptrace(PTRACE_PEEKUSER, tid, |
284 | reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3); |
285 | sys_ptrace(PTRACE_PEEKUSER, tid, |
286 | reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp); |
287 | #endif |
288 | |
289 | const uint8_t* stack_pointer; |
290 | #if defined(__i386) |
291 | my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); |
292 | #elif defined(__x86_64) |
293 | my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); |
294 | #elif defined(__ARM_EABI__) |
295 | my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); |
296 | #elif defined(__aarch64__) |
297 | my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); |
298 | #elif defined(__mips__) |
299 | stack_pointer = |
300 | reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); |
301 | #else |
302 | #error "This code hasn't been ported to your platform yet." |
303 | #endif |
304 | info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); |
305 | |
306 | return true; |
307 | } |
308 | |
309 | bool LinuxPtraceDumper::IsPostMortem() const { |
310 | return false; |
311 | } |
312 | |
313 | bool LinuxPtraceDumper::ThreadsSuspend() { |
314 | if (threads_suspended_) |
315 | return true; |
316 | for (size_t i = 0; i < threads_.size(); ++i) { |
317 | if (!SuspendThread(threads_[i])) { |
318 | // If the thread either disappeared before we could attach to it, or if |
319 | // it was part of the seccomp sandbox's trusted code, it is OK to |
320 | // silently drop it from the minidump. |
321 | if (i < threads_.size() - 1) { |
322 | my_memmove(&threads_[i], &threads_[i + 1], |
323 | (threads_.size() - i - 1) * sizeof(threads_[i])); |
324 | } |
325 | threads_.resize(threads_.size() - 1); |
326 | --i; |
327 | } |
328 | } |
329 | threads_suspended_ = true; |
330 | return threads_.size() > 0; |
331 | } |
332 | |
333 | bool LinuxPtraceDumper::ThreadsResume() { |
334 | if (!threads_suspended_) |
335 | return false; |
336 | bool good = true; |
337 | for (size_t i = 0; i < threads_.size(); ++i) |
338 | good &= ResumeThread(threads_[i]); |
339 | threads_suspended_ = false; |
340 | return good; |
341 | } |
342 | |
343 | // Parse /proc/$pid/task to list all the threads of the process identified by |
344 | // pid. |
345 | bool LinuxPtraceDumper::EnumerateThreads() { |
346 | char task_path[NAME_MAX]; |
347 | if (!BuildProcPath(task_path, pid_, "task" )) |
348 | return false; |
349 | |
350 | const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); |
351 | if (fd < 0) |
352 | return false; |
353 | DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); |
354 | |
355 | // The directory may contain duplicate entries which we filter by assuming |
356 | // that they are consecutive. |
357 | int last_tid = -1; |
358 | const char* dent_name; |
359 | while (dir_reader->GetNextEntry(&dent_name)) { |
360 | if (my_strcmp(dent_name, "." ) && |
361 | my_strcmp(dent_name, ".." )) { |
362 | int tid = 0; |
363 | if (my_strtoui(&tid, dent_name) && |
364 | last_tid != tid) { |
365 | last_tid = tid; |
366 | threads_.push_back(tid); |
367 | } |
368 | } |
369 | dir_reader->PopEntry(); |
370 | } |
371 | |
372 | sys_close(fd); |
373 | return true; |
374 | } |
375 | |
376 | } // namespace google_breakpad |
377 | |