OLD | NEW |
---|---|
1 // Copyright (c) 2013 Google Inc. | 1 // Copyright (c) 2013 Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 18 matching lines...) Loading... | |
29 | 29 |
30 // exploitability_linux.cc: Linux specific exploitability engine. | 30 // exploitability_linux.cc: Linux specific exploitability engine. |
31 // | 31 // |
32 // Provides a guess at the exploitability of the crash for the Linux | 32 // Provides a guess at the exploitability of the crash for the Linux |
33 // platform given a minidump and process_state. | 33 // platform given a minidump and process_state. |
34 // | 34 // |
35 // Author: Matthew Riley | 35 // Author: Matthew Riley |
36 | 36 |
37 #include "processor/exploitability_linux.h" | 37 #include "processor/exploitability_linux.h" |
38 | 38 |
39 #include <regex.h> | |
40 #include <stdio.h> | |
41 #include <string.h> | |
42 #include <sysexits.h> | |
43 #include <sys/types.h> | |
44 #include <sys/wait.h> | |
45 | |
46 #include <sstream> | |
47 #include <iterator> | |
48 | |
39 #include "google_breakpad/common/minidump_exception_linux.h" | 49 #include "google_breakpad/common/minidump_exception_linux.h" |
40 #include "google_breakpad/processor/call_stack.h" | 50 #include "google_breakpad/processor/call_stack.h" |
41 #include "google_breakpad/processor/process_state.h" | 51 #include "google_breakpad/processor/process_state.h" |
42 #include "google_breakpad/processor/stack_frame.h" | 52 #include "google_breakpad/processor/stack_frame.h" |
43 #include "processor/logging.h" | 53 #include "processor/logging.h" |
44 | 54 |
55 #define MAX_INSTRUCTION_LEN 15 | |
56 // This is the buffer size for objdump's output. | |
57 #define MAX_OBJDUMP_BUFFER_LEN 4096 | |
58 | |
45 namespace { | 59 namespace { |
46 | 60 |
47 // This function in libc is called if the program was compiled with | 61 // This function in libc is called if the program was compiled with |
48 // -fstack-protector and a function's stack canary changes. | 62 // -fstack-protector and a function's stack canary changes. |
49 const char kStackCheckFailureFunction[] = "__stack_chk_fail"; | 63 const char kStackCheckFailureFunction[] = "__stack_chk_fail"; |
50 | 64 |
51 // This function in libc is called if the program was compiled with | 65 // This function in libc is called if the program was compiled with |
52 // -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime | 66 // -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime |
53 // can determine that the call would overflow the target buffer. | 67 // can determine that the call would overflow the target buffer. |
54 const char kBoundsCheckFailureFunction[] = "__chk_fail"; | 68 const char kBoundsCheckFailureFunction[] = "__chk_fail"; |
55 | 69 |
70 #ifndef _WIN32 | |
71 // PID of child process that is forked to call objdump. | |
72 pid_t child_pid = -1; | |
73 | |
74 // Function to kill the child process used to call objdump in case the child | |
75 // process hangs. | |
76 void kill_child(int signal) { | |
77 kill(child_pid, SIGKILL); | |
78 } | |
79 #endif // _WIN32 | |
80 | |
56 } // namespace | 81 } // namespace |
57 | 82 |
58 namespace google_breakpad { | 83 namespace google_breakpad { |
59 | 84 |
60 ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, | 85 ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, |
61 ProcessState *process_state) | 86 ProcessState *process_state) |
62 : Exploitability(dump, process_state) { } | 87 : Exploitability(dump, process_state) { } |
63 | 88 |
64 ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() { | 89 ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() { |
65 // Check the crashing thread for functions suggesting a buffer overflow or | 90 // Check the crashing thread for functions suggesting a buffer overflow or |
(...skipping 49 matching lines...) Loading... | |
115 BPLOG(INFO) << "Failed to retrieve instruction pointer."; | 140 BPLOG(INFO) << "Failed to retrieve instruction pointer."; |
116 return EXPLOITABILITY_ERR_PROCESSING; | 141 return EXPLOITABILITY_ERR_PROCESSING; |
117 } | 142 } |
118 | 143 |
119 // Getting the stack pointer. | 144 // Getting the stack pointer. |
120 if (!context->GetStackPointer(&stack_ptr)) { | 145 if (!context->GetStackPointer(&stack_ptr)) { |
121 BPLOG(INFO) << "Failed to retrieve stack pointer."; | 146 BPLOG(INFO) << "Failed to retrieve stack pointer."; |
122 return EXPLOITABILITY_ERR_PROCESSING; | 147 return EXPLOITABILITY_ERR_PROCESSING; |
123 } | 148 } |
124 | 149 |
125 // Checking for the instruction pointer in a valid instruction region. | 150 // Checking for the instruction pointer in a valid instruction region |
151 // or if the crash resulted during an illegal write. | |
126 if (!this->InstructionPointerInCode(instruction_ptr) || | 152 if (!this->InstructionPointerInCode(instruction_ptr) || |
127 this->StackPointerOffStack(stack_ptr) || | 153 this->StackPointerOffStack(stack_ptr) || |
128 this->ExecutableStackOrHeap()) { | 154 this->ExecutableStackOrHeap() || |
155 this->EndedOnIllegalWrite(instruction_ptr)) { | |
129 return EXPLOITABILITY_HIGH; | 156 return EXPLOITABILITY_HIGH; |
130 } | 157 } |
131 | 158 |
132 // There was no strong evidence suggesting exploitability, but the minidump | 159 // There was no strong evidence suggesting exploitability, but the minidump |
133 // does not appear totally benign either. | 160 // does not appear totally benign either. |
134 return EXPLOITABILITY_INTERESTING; | 161 return EXPLOITABILITY_INTERESTING; |
135 } | 162 } |
136 | 163 |
164 bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) { | |
ivanpe
2015/08/20 05:37:44
I think that this functionality should be disabled
liuandrew
2015/08/20 17:11:51
Done.
| |
165 #ifdef _WIN32 | |
166 BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method."; | |
167 #else // _WIN32 | |
168 // Get memory region containing instruction pointer. | |
169 MinidumpMemoryList *memory_list = dump_->GetMemoryList(); | |
170 MinidumpMemoryRegion *memory_region = | |
171 memory_list ? | |
172 memory_list->GetMemoryRegionForAddress(instruction_ptr) : NULL; | |
173 if (!memory_region) { | |
174 BPLOG(ERROR) << "No memory region around instruction pointer."; | |
ivanpe
2015/08/20 05:37:44
Please, change this to INFO. In general we want t
liuandrew
2015/08/20 17:11:51
Done.
| |
175 return false; | |
176 } | |
177 | |
178 // Get exception data to find architecture. | |
179 string architecture = ""; | |
180 MinidumpException *exception = dump_->GetException(); | |
181 // This should never evaluate to true, since this should not be reachable | |
182 // without checking for exception data earlier. | |
183 if (!exception) { | |
184 BPLOG(INFO) << "No exception data."; | |
185 return false; | |
186 } | |
187 const MDRawExceptionStream *raw_exception_stream = exception->exception(); | |
188 const MinidumpContext *context = exception->GetContext(); | |
189 // This should not evaluate to true, for the same reason mentioned above. | |
190 if (!raw_exception_stream || !context) { | |
191 BPLOG(INFO) << "No exception or architecture data."; | |
192 return false; | |
193 } | |
194 // Check architecture and set architecture variable to corresponding flag | |
195 // in objdump. | |
196 switch (context->GetContextCPU()) { | |
197 case MD_CONTEXT_X86: | |
198 architecture = "i386"; | |
199 break; | |
200 case MD_CONTEXT_AMD64: | |
201 architecture = "i386:x86-64"; | |
202 break; | |
203 default: | |
204 // Unsupported architecture. Note that ARM architectures are not | |
205 // supported because objdump does not support ARM. | |
206 return false; | |
207 break; | |
208 } | |
209 | |
210 // Get memory region around instruction pointer and the number of bytes | |
211 // before and after the instruction pointer in the memory region. | |
212 const uint8_t *raw_memory = memory_region->GetMemory(); | |
213 const uint64_t base = memory_region->GetBase(); | |
214 if (base > instruction_ptr) { | |
215 BPLOG(ERROR) << "Memory region base value exceeds instruction pointer."; | |
216 return false; | |
217 } | |
218 const uint64_t offset = instruction_ptr - base; | |
219 if (memory_region->GetSize() < MAX_INSTRUCTION_LEN + offset) { | |
220 BPLOG(ERROR) << "Not enough bytes left to guarantee complete instruction."; | |
ivanpe
2015/08/20 05:37:43
Please, replace with INFO
liuandrew
2015/08/20 17:11:51
Done.
| |
221 return false; | |
222 } | |
223 | |
224 // Convert bytes into objdump output. | |
225 char objdump_output_buffer[MAX_OBJDUMP_BUFFER_LEN] = {0}; | |
226 DisassembleBytes(architecture, | |
227 raw_memory + offset, | |
228 MAX_OBJDUMP_BUFFER_LEN, | |
229 objdump_output_buffer); | |
230 | |
231 // Set up regular expression to catch first instruction from objdump. | |
232 // The line with the instruction will begin with "0:". | |
233 regex_t regex; | |
234 regcomp(®ex, "0:", REG_EXTENDED | REG_NOSUB); | |
ivanpe
2015/08/20 05:37:44
1. You are not checking the return value.
2. Isn'
liuandrew
2015/08/20 17:11:51
Done.
| |
235 | |
236 // Put buffer data into stream to output line-by-line. | |
237 std::stringstream objdump_stream; | |
238 objdump_stream.str(string(objdump_output_buffer)); | |
239 string line; | |
240 | |
241 // Pipe each output line into the string until the string contains | |
242 // the first instruction from objdump. | |
243 // Loop until the line matches the regex or there are no lines left. | |
244 do { | |
245 if (!getline(objdump_stream, line)) { | |
246 BPLOG(ERROR) << "Objdump instructions not found"; | |
ivanpe
2015/08/20 05:37:44
please change this to LOG
liuandrew
2015/08/20 17:11:51
Done.
| |
247 return false; | |
ivanpe
2015/08/20 05:37:44
Don't you need to free regex?
liuandrew
2015/08/20 17:11:51
Removed since I'm now using string::find.
| |
248 } | |
249 } while (regexec(®ex, line.c_str(), 0, NULL, 0)); | |
250 regfree(®ex); // Free regex data. | |
251 | |
252 // Convert objdump instruction line into the operation and operands. | |
253 string instruction = ""; | |
254 string dest = ""; | |
255 string src = ""; | |
256 TokenizeObjdumpInstruction(line, &instruction, &dest, &src); | |
257 | |
ivanpe
2015/08/20 05:37:44
Please use a single empty line here.
liuandrew
2015/08/20 17:11:51
Done.
| |
258 | |
259 // Check if the operation is a write to memory. First, the instruction | |
260 // must one that can write to memory. Second, the write destination | |
261 // must be a spot in memory rather than a register. Since there are no | |
262 // symbols from objdump, the destination will be enclosed by brackets. | |
263 if (dest.size() > 2 && dest.at(0) == '[' && dest.at(dest.size() - 1) == ']' && | |
264 (!instruction.compare("mov") || !instruction.compare("inc") || | |
265 !instruction.compare("dec") || !instruction.compare("and") || | |
266 !instruction.compare("or") || !instruction.compare("xor") || | |
267 !instruction.compare("not") || !instruction.compare("neg") || | |
268 !instruction.compare("add") || !instruction.compare("sub") || | |
269 !instruction.compare("shl") || !instruction.compare("shr"))) { | |
270 // Strip away enclosing brackets from the destination address. | |
271 dest = dest.substr(1, dest.size() - 2); | |
272 uint64_t write_address = 0; | |
273 CalculateAddress(dest, *context, &write_address); | |
274 | |
275 // If the program crashed as a result of a write, the destination of | |
276 // the write must have been an address that did not permit writing. | |
277 // However, if the address is under 4k, due to program protections, | |
278 // the crash does not suggest exploitability for writes with such a | |
279 // low target address. | |
280 return write_address > 4096; | |
281 } | |
282 #endif // _WIN32 | |
283 return false; | |
284 } | |
285 | |
286 #ifndef _WIN32 | |
287 bool ExploitabilityLinux::CalculateAddress(const string &address_expression, | |
288 const DumpContext &context, | |
289 uint64_t *write_address) { | |
290 // The destination should be the format reg+a or reg-a, where reg | |
291 // is a register and a is a hexadecimal constant. Although more complex | |
292 // expressions can make valid instructions, objdump's disassembly outputs | |
293 // it in this simpler format. | |
294 // TODO(liuandrew): Handle more complex formats, should they arise. | |
295 | |
296 if (!write_address) { | |
297 BPLOG(ERROR) << "Null parameter."; | |
298 return false; | |
299 } | |
300 | |
301 // Clone parameter into a non-const string. | |
302 string expression = address_expression; | |
303 | |
304 // Parse out the constant that is added to the address (if it exists). | |
305 size_t delim = expression.find('+'); | |
306 bool positive_add_constant = true; | |
307 // Check if constant is subtracted instead of added. | |
308 if (delim == string::npos) { | |
309 positive_add_constant = false; | |
310 delim = expression.find('-'); | |
311 } | |
312 uint32_t add_constant = 0; | |
313 // Save constant and remove it from the expression. | |
314 if (delim != string::npos) { | |
315 if (!sscanf(expression.substr(delim + 1).c_str(), "%x", &add_constant)) { | |
316 BPLOG(ERROR) << "Failed to scan constant."; | |
317 return false; | |
318 } | |
319 expression = expression.substr(0, delim); | |
320 } | |
321 | |
322 // Set the the write address to the corresponding register. | |
323 // TODO(liuandrew): Add support for partial registers, such as | |
324 // the rax/eax/ax/ah/al chain. | |
325 switch (context.GetContextCPU()) { | |
326 case MD_CONTEXT_X86: | |
327 if (!expression.compare("eax")) { | |
328 *write_address = context.GetContextX86()->eax; | |
329 } else if (!expression.compare("ebx")) { | |
330 *write_address = context.GetContextX86()->ebx; | |
331 } else if (!expression.compare("ecx")) { | |
332 *write_address = context.GetContextX86()->ecx; | |
333 } else if (!expression.compare("edx")) { | |
334 *write_address = context.GetContextX86()->edx; | |
335 } else if (!expression.compare("edi")) { | |
336 *write_address = context.GetContextX86()->edi; | |
337 } else if (!expression.compare("esi")) { | |
338 *write_address = context.GetContextX86()->esi; | |
339 } else if (!expression.compare("ebp")) { | |
340 *write_address = context.GetContextX86()->ebp; | |
341 } else if (!expression.compare("esp")) { | |
342 *write_address = context.GetContextX86()->esp; | |
343 } else if (!expression.compare("eip")) { | |
344 *write_address = context.GetContextX86()->eip; | |
345 } else { | |
346 BPLOG(ERROR) << "Unsupported register"; | |
347 return false; | |
348 } | |
349 break; | |
350 case MD_CONTEXT_AMD64: | |
351 if (!expression.compare("rax")) { | |
352 *write_address = context.GetContextAMD64()->rax; | |
353 } else if (!expression.compare("rbx")) { | |
354 *write_address = context.GetContextAMD64()->rbx; | |
355 } else if (!expression.compare("rcx")) { | |
356 *write_address = context.GetContextAMD64()->rcx; | |
357 } else if (!expression.compare("rdx")) { | |
358 *write_address = context.GetContextAMD64()->rdx; | |
359 } else if (!expression.compare("rdi")) { | |
360 *write_address = context.GetContextAMD64()->rdi; | |
361 } else if (!expression.compare("rsi")) { | |
362 *write_address = context.GetContextAMD64()->rsi; | |
363 } else if (!expression.compare("rbp")) { | |
364 *write_address = context.GetContextAMD64()->rbp; | |
365 } else if (!expression.compare("rsp")) { | |
366 *write_address = context.GetContextAMD64()->rsp; | |
367 } else if (!expression.compare("rip")) { | |
368 *write_address = context.GetContextAMD64()->rip; | |
369 } else if (!expression.compare("r8")) { | |
370 *write_address = context.GetContextAMD64()->r8; | |
371 } else if (!expression.compare("r9")) { | |
372 *write_address = context.GetContextAMD64()->r9; | |
373 } else if (!expression.compare("r10")) { | |
374 *write_address = context.GetContextAMD64()->r10; | |
375 } else if (!expression.compare("r11")) { | |
376 *write_address = context.GetContextAMD64()->r11; | |
377 } else if (!expression.compare("r12")) { | |
378 *write_address = context.GetContextAMD64()->r12; | |
379 } else if (!expression.compare("r13")) { | |
380 *write_address = context.GetContextAMD64()->r13; | |
381 } else if (!expression.compare("r14")) { | |
382 *write_address = context.GetContextAMD64()->r14; | |
383 } else if (!expression.compare("r15")) { | |
384 *write_address = context.GetContextAMD64()->r15; | |
385 } else { | |
386 BPLOG(ERROR) << "Unsupported register"; | |
387 return false; | |
388 } | |
389 break; | |
390 default: | |
391 // This should not occur since the same switch condition | |
392 // should have terminated this method. | |
393 return false; | |
394 break; | |
395 } | |
396 | |
397 // Add or subtract constant from write address (if applicable). | |
398 *write_address = | |
399 positive_add_constant ? | |
400 *write_address + add_constant : *write_address - add_constant; | |
401 | |
402 return true; | |
403 } | |
404 | |
405 bool ExploitabilityLinux::TokenizeObjdumpInstruction(const string &line, | |
406 string *operation, | |
407 string *dest, | |
408 string *src) { | |
409 std::cout << "LINE: " << line << std::endl; | |
ivanpe
2015/08/20 05:37:44
Please, do not output to console.
liuandrew
2015/08/20 17:11:51
Done.
My bad, I accidentally left it here while d
| |
410 if (!operation || !dest || !src) { | |
411 BPLOG(ERROR) << "Null parameters passed."; | |
412 return false; | |
413 } | |
414 | |
415 // Set all pointer values to empty strings. | |
416 *operation = ""; | |
417 *dest = ""; | |
418 *src = ""; | |
419 | |
420 // Tokenize the objdump line. | |
421 vector<string> tokens; | |
422 std::istringstream line_stream(line); | |
423 copy(std::istream_iterator<string>(line_stream), | |
424 std::istream_iterator<string>(), | |
425 std::back_inserter(tokens)); | |
426 | |
427 // Regex for the data in hex form. Each byte is two hex digits. | |
428 regex_t regex; | |
429 regcomp(®ex, "^[[:xdigit:]]{2}$", REG_EXTENDED | REG_NOSUB); | |
430 | |
431 // Find and set the location of the operator. The operator appears | |
432 // directly after the chain of bytes that define the instruction. The | |
433 // operands will be the last token, given that the instruction has operands. | |
434 // If not, the operator is the last token. The loop skips the first token | |
435 // because the first token is the instruction number (namely "0:"). | |
436 string operands = ""; | |
437 for (size_t i = 1; i < tokens.size(); i++) { | |
438 // Check if current token no longer is in byte format. | |
439 if (regexec(®ex, tokens[i].c_str(), 0, NULL, 0)) { | |
440 // instruction = tokens[i]; | |
441 *operation = tokens[i]; | |
442 // If the operator is the last token, there are no operands. | |
443 if (i != tokens.size() - 1) { | |
444 operands = tokens[tokens.size() - 1]; | |
445 } | |
446 break; | |
447 } | |
448 } | |
449 regfree(®ex); | |
450 | |
451 if (operation->empty()) { | |
452 BPLOG(ERROR) << "Failed to parse out operation from objdump instruction."; | |
453 return false; | |
454 } | |
455 | |
456 // Split operands into source and destination (if applicable). | |
457 if (!operands.empty()) { | |
458 size_t delim = operands.find(','); | |
459 if (delim == string::npos) { | |
460 *dest = operands; | |
461 } else { | |
462 *dest = operands.substr(0, delim); | |
463 *src = operands.substr(delim + 1); | |
464 } | |
465 } | |
466 return true; | |
467 } | |
468 | |
469 bool ExploitabilityLinux::DisassembleBytes(const string &architecture, | |
470 const uint8_t *raw_bytes, | |
471 const unsigned int buffer_len, | |
472 char *objdump_output_buffer) { | |
473 int pipe_fd[2]; | |
474 | |
475 if (!raw_bytes || !objdump_output_buffer) { | |
476 BPLOG(ERROR) << "Bad input parameters."; | |
477 return false; | |
478 } | |
479 | |
480 // Write raw bytes around instruction pointer to a temporary file to | |
481 // pass as an argument to objdump. | |
482 char raw_bytes_tmpfile[] = "/tmp/breakpad_mem_region-raw_bytes-XXXXXX"; | |
483 int raw_bytes_fd = mkstemp(raw_bytes_tmpfile); | |
484 if (raw_bytes_fd < 0) { | |
485 BPLOG(ERROR) << "Failed to create tempfile."; | |
486 unlink(raw_bytes_tmpfile); | |
487 return false; | |
488 } | |
489 if (write(raw_bytes_fd, raw_bytes, MAX_INSTRUCTION_LEN) | |
490 != MAX_INSTRUCTION_LEN) { | |
491 BPLOG(ERROR) << "Writing of raw bytes failed."; | |
492 unlink(raw_bytes_tmpfile); | |
493 return false; | |
494 } | |
495 | |
496 // Open pipe between STDOUT and the objdump output buffer. | |
497 if (pipe(pipe_fd) < 0) { | |
498 BPLOG(ERROR) << "Failed to pipe."; | |
499 return false; | |
500 } | |
501 | |
502 // Fork process to call objdump. | |
503 if ((child_pid = fork()) < 0) { | |
504 BPLOG(ERROR) << "Forking failed."; | |
505 return false; | |
506 } | |
507 | |
508 if (child_pid) { // Parent code. | |
509 close(pipe_fd[1]); | |
510 | |
511 // Set alarm to kill child process after one second in case it hangs. | |
512 signal(SIGALRM, (void (*)(int))kill_child); | |
513 alarm(1); | |
514 | |
515 // Read piped output from objdump. | |
516 ssize_t bytes_read = read(pipe_fd[0], | |
517 objdump_output_buffer, | |
518 buffer_len); | |
519 int status; | |
520 wait(&status); // Wait for child process to run objdump. | |
521 alarm(0); // Disable alarm. | |
522 if (WEXITSTATUS(status)) { | |
523 BPLOG(ERROR) << "Child process encountered error."; | |
524 unlink(raw_bytes_tmpfile); | |
525 return false; | |
526 } | |
527 if (bytes_read < 0) { | |
528 BPLOG(ERROR) << "Failed to read objdump output."; | |
529 unlink(raw_bytes_tmpfile); | |
530 return false; | |
531 } | |
532 close(pipe_fd[0]); | |
533 } else { // Child code. | |
534 close(pipe_fd[0]); | |
535 dup2(pipe_fd[1], STDOUT_FILENO); // Send objdump output across pipe. | |
536 // Exec objdump. | |
537 execlp("objdump", "objdump", "-D", "-b", "binary", "-M", "intel", | |
538 "-m", architecture.c_str(), raw_bytes_tmpfile, NULL); | |
539 BPLOG(ERROR) << "Exec failed."; | |
540 exit(EX_OSERR); | |
541 } | |
542 unlink(raw_bytes_tmpfile); | |
543 return true; | |
544 } | |
545 #endif // _WIN32 | |
546 | |
137 bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) { | 547 bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) { |
138 MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); | 548 MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); |
139 // Inconclusive if there are no mappings available. | 549 // Inconclusive if there are no mappings available. |
140 if (!linux_maps_list) { | 550 if (!linux_maps_list) { |
141 return false; | 551 return false; |
142 } | 552 } |
143 const MinidumpLinuxMaps *linux_maps = | 553 const MinidumpLinuxMaps *linux_maps = |
144 linux_maps_list->GetLinuxMapsForAddress(stack_ptr); | 554 linux_maps_list->GetLinuxMapsForAddress(stack_ptr); |
145 // Checks if the stack pointer maps to a valid mapping and if the mapping | 555 // Checks if the stack pointer maps to a valid mapping and if the mapping |
146 // is not the stack. If the mapping has no name, it is inconclusive whether | 556 // is not the stack. If the mapping has no name, it is inconclusive whether |
(...skipping 70 matching lines...) Loading... | |
217 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: | 627 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: |
218 return true; | 628 return true; |
219 break; | 629 break; |
220 default: | 630 default: |
221 return false; | 631 return false; |
222 break; | 632 break; |
223 } | 633 } |
224 } | 634 } |
225 | 635 |
226 } // namespace google_breakpad | 636 } // namespace google_breakpad |
OLD | NEW |