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...) Expand all 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 #ifndef _WIN32 |
| 40 #include <regex.h> |
| 41 #include <stdio.h> |
| 42 #include <stdlib.h> |
| 43 #include <string.h> |
| 44 |
| 45 #include <sstream> |
| 46 #include <iterator> |
| 47 #endif // _WIN32 |
| 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 |
45 namespace { | 55 namespace { |
46 | 56 |
47 // This function in libc is called if the program was compiled with | 57 // This function in libc is called if the program was compiled with |
48 // -fstack-protector and a function's stack canary changes. | 58 // -fstack-protector and a function's stack canary changes. |
49 const char kStackCheckFailureFunction[] = "__stack_chk_fail"; | 59 const char kStackCheckFailureFunction[] = "__stack_chk_fail"; |
50 | 60 |
51 // 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 |
52 // -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime | 62 // -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime |
53 // can determine that the call would overflow the target buffer. | 63 // can determine that the call would overflow the target buffer. |
54 const char kBoundsCheckFailureFunction[] = "__chk_fail"; | 64 const char kBoundsCheckFailureFunction[] = "__chk_fail"; |
55 | 65 |
| 66 #ifndef _WIN32 |
| 67 const unsigned int MAX_INSTRUCTION_LEN = 15; |
| 68 const unsigned int MAX_OBJDUMP_BUFFER_LEN = 4096; |
| 69 #endif // _WIN32 |
| 70 |
56 } // namespace | 71 } // namespace |
57 | 72 |
58 namespace google_breakpad { | 73 namespace google_breakpad { |
59 | 74 |
60 ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, | 75 ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, |
61 ProcessState *process_state) | 76 ProcessState *process_state) |
62 : Exploitability(dump, process_state) { } | 77 : Exploitability(dump, process_state), |
| 78 enable_objdump_(false) { } |
| 79 |
| 80 ExploitabilityLinux::ExploitabilityLinux(Minidump *dump, |
| 81 ProcessState *process_state, |
| 82 bool enable_objdump) |
| 83 : Exploitability(dump, process_state), |
| 84 enable_objdump_(enable_objdump) { } |
| 85 |
63 | 86 |
64 ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() { | 87 ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() { |
65 // Check the crashing thread for functions suggesting a buffer overflow or | 88 // Check the crashing thread for functions suggesting a buffer overflow or |
66 // stack smash. | 89 // stack smash. |
67 if (process_state_->requesting_thread() != -1) { | 90 if (process_state_->requesting_thread() != -1) { |
68 CallStack* crashing_thread = | 91 CallStack* crashing_thread = |
69 process_state_->threads()->at(process_state_->requesting_thread()); | 92 process_state_->threads()->at(process_state_->requesting_thread()); |
70 const vector<StackFrame*>& crashing_thread_frames = | 93 const vector<StackFrame*>& crashing_thread_frames = |
71 *crashing_thread->frames(); | 94 *crashing_thread->frames(); |
72 for (size_t i = 0; i < crashing_thread_frames.size(); ++i) { | 95 for (size_t i = 0; i < crashing_thread_frames.size(); ++i) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 BPLOG(INFO) << "Failed to retrieve instruction pointer."; | 138 BPLOG(INFO) << "Failed to retrieve instruction pointer."; |
116 return EXPLOITABILITY_ERR_PROCESSING; | 139 return EXPLOITABILITY_ERR_PROCESSING; |
117 } | 140 } |
118 | 141 |
119 // Getting the stack pointer. | 142 // Getting the stack pointer. |
120 if (!context->GetStackPointer(&stack_ptr)) { | 143 if (!context->GetStackPointer(&stack_ptr)) { |
121 BPLOG(INFO) << "Failed to retrieve stack pointer."; | 144 BPLOG(INFO) << "Failed to retrieve stack pointer."; |
122 return EXPLOITABILITY_ERR_PROCESSING; | 145 return EXPLOITABILITY_ERR_PROCESSING; |
123 } | 146 } |
124 | 147 |
125 // Checking for the instruction pointer in a valid instruction region. | 148 // Checking for the instruction pointer in a valid instruction region, |
| 149 // a misplaced stack pointer, and an executable stack or heap. |
126 if (!this->InstructionPointerInCode(instruction_ptr) || | 150 if (!this->InstructionPointerInCode(instruction_ptr) || |
127 this->StackPointerOffStack(stack_ptr) || | 151 this->StackPointerOffStack(stack_ptr) || |
128 this->ExecutableStackOrHeap()) { | 152 this->ExecutableStackOrHeap()) { |
129 return EXPLOITABILITY_HIGH; | 153 return EXPLOITABILITY_HIGH; |
130 } | 154 } |
131 | 155 |
| 156 // Check for write to read only memory or invalid memory, shelling out |
| 157 // to objdump is enabled. |
| 158 if (enable_objdump_ && this->EndedOnIllegalWrite(instruction_ptr)) { |
| 159 return EXPLOITABILITY_HIGH; |
| 160 } |
| 161 |
132 // There was no strong evidence suggesting exploitability, but the minidump | 162 // There was no strong evidence suggesting exploitability, but the minidump |
133 // does not appear totally benign either. | 163 // does not appear totally benign either. |
134 return EXPLOITABILITY_INTERESTING; | 164 return EXPLOITABILITY_INTERESTING; |
135 } | 165 } |
136 | 166 |
| 167 bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) { |
| 168 #ifdef _WIN32 |
| 169 BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method."; |
| 170 #else |
| 171 // Get memory region containing instruction pointer. |
| 172 MinidumpMemoryList *memory_list = dump_->GetMemoryList(); |
| 173 MinidumpMemoryRegion *memory_region = |
| 174 memory_list ? |
| 175 memory_list->GetMemoryRegionForAddress(instruction_ptr) : NULL; |
| 176 if (!memory_region) { |
| 177 BPLOG(INFO) << "No memory region around instruction pointer."; |
| 178 return false; |
| 179 } |
| 180 |
| 181 // Get exception data to find architecture. |
| 182 string architecture = ""; |
| 183 MinidumpException *exception = dump_->GetException(); |
| 184 // This should never evaluate to true, since this should not be reachable |
| 185 // without checking for exception data earlier. |
| 186 if (!exception) { |
| 187 BPLOG(INFO) << "No exception data."; |
| 188 return false; |
| 189 } |
| 190 const MDRawExceptionStream *raw_exception_stream = exception->exception(); |
| 191 const MinidumpContext *context = exception->GetContext(); |
| 192 // This should not evaluate to true, for the same reason mentioned above. |
| 193 if (!raw_exception_stream || !context) { |
| 194 BPLOG(INFO) << "No exception or architecture data."; |
| 195 return false; |
| 196 } |
| 197 // Check architecture and set architecture variable to corresponding flag |
| 198 // in objdump. |
| 199 switch (context->GetContextCPU()) { |
| 200 case MD_CONTEXT_X86: |
| 201 architecture = "i386"; |
| 202 break; |
| 203 case MD_CONTEXT_AMD64: |
| 204 architecture = "i386:x86-64"; |
| 205 break; |
| 206 default: |
| 207 // Unsupported architecture. Note that ARM architectures are not |
| 208 // supported because objdump does not support ARM. |
| 209 return false; |
| 210 break; |
| 211 } |
| 212 |
| 213 // Get memory region around instruction pointer and the number of bytes |
| 214 // before and after the instruction pointer in the memory region. |
| 215 const uint8_t *raw_memory = memory_region->GetMemory(); |
| 216 const uint64_t base = memory_region->GetBase(); |
| 217 if (base > instruction_ptr) { |
| 218 BPLOG(ERROR) << "Memory region base value exceeds instruction pointer."; |
| 219 return false; |
| 220 } |
| 221 const uint64_t offset = instruction_ptr - base; |
| 222 if (memory_region->GetSize() < MAX_INSTRUCTION_LEN + offset) { |
| 223 BPLOG(INFO) << "Not enough bytes left to guarantee complete instruction."; |
| 224 return false; |
| 225 } |
| 226 |
| 227 // Convert bytes into objdump output. |
| 228 char objdump_output_buffer[MAX_OBJDUMP_BUFFER_LEN] = {0}; |
| 229 DisassembleBytes(architecture, |
| 230 raw_memory + offset, |
| 231 MAX_OBJDUMP_BUFFER_LEN, |
| 232 objdump_output_buffer); |
| 233 |
| 234 // Put buffer data into stream to output line-by-line. |
| 235 std::stringstream objdump_stream; |
| 236 objdump_stream.str(string(objdump_output_buffer)); |
| 237 string line; |
| 238 |
| 239 // Pipe each output line into the string until the string contains |
| 240 // the first instruction from objdump. |
| 241 // Loop until the line shows the first instruction or there are no lines left. |
| 242 do { |
| 243 if (!getline(objdump_stream, line)) { |
| 244 BPLOG(INFO) << "Objdump instructions not found"; |
| 245 return false; |
| 246 } |
| 247 } while (line.find("0:") == string::npos); |
| 248 // This first instruction contains the above substring. |
| 249 |
| 250 // Convert objdump instruction line into the operation and operands. |
| 251 string instruction = ""; |
| 252 string dest = ""; |
| 253 string src = ""; |
| 254 TokenizeObjdumpInstruction(line, &instruction, &dest, &src); |
| 255 |
| 256 // Check if the operation is a write to memory. First, the instruction |
| 257 // must one that can write to memory. Second, the write destination |
| 258 // must be a spot in memory rather than a register. Since there are no |
| 259 // symbols from objdump, the destination will be enclosed by brackets. |
| 260 if (dest.size() > 2 && dest.at(0) == '[' && dest.at(dest.size() - 1) == ']' && |
| 261 (!instruction.compare("mov") || !instruction.compare("inc") || |
| 262 !instruction.compare("dec") || !instruction.compare("and") || |
| 263 !instruction.compare("or") || !instruction.compare("xor") || |
| 264 !instruction.compare("not") || !instruction.compare("neg") || |
| 265 !instruction.compare("add") || !instruction.compare("sub") || |
| 266 !instruction.compare("shl") || !instruction.compare("shr"))) { |
| 267 // Strip away enclosing brackets from the destination address. |
| 268 dest = dest.substr(1, dest.size() - 2); |
| 269 uint64_t write_address = 0; |
| 270 CalculateAddress(dest, *context, &write_address); |
| 271 |
| 272 // If the program crashed as a result of a write, the destination of |
| 273 // the write must have been an address that did not permit writing. |
| 274 // However, if the address is under 4k, due to program protections, |
| 275 // the crash does not suggest exploitability for writes with such a |
| 276 // low target address. |
| 277 return write_address > 4096; |
| 278 } |
| 279 #endif // _WIN32 |
| 280 return false; |
| 281 } |
| 282 |
| 283 #ifndef _WIN32 |
| 284 bool ExploitabilityLinux::CalculateAddress(const string &address_expression, |
| 285 const DumpContext &context, |
| 286 uint64_t *write_address) { |
| 287 // The destination should be the format reg+a or reg-a, where reg |
| 288 // is a register and a is a hexadecimal constant. Although more complex |
| 289 // expressions can make valid instructions, objdump's disassembly outputs |
| 290 // it in this simpler format. |
| 291 // TODO(liuandrew): Handle more complex formats, should they arise. |
| 292 |
| 293 if (!write_address) { |
| 294 BPLOG(ERROR) << "Null parameter."; |
| 295 return false; |
| 296 } |
| 297 |
| 298 // Clone parameter into a non-const string. |
| 299 string expression = address_expression; |
| 300 |
| 301 // Parse out the constant that is added to the address (if it exists). |
| 302 size_t delim = expression.find('+'); |
| 303 bool positive_add_constant = true; |
| 304 // Check if constant is subtracted instead of added. |
| 305 if (delim == string::npos) { |
| 306 positive_add_constant = false; |
| 307 delim = expression.find('-'); |
| 308 } |
| 309 uint32_t add_constant = 0; |
| 310 // Save constant and remove it from the expression. |
| 311 if (delim != string::npos) { |
| 312 if (!sscanf(expression.substr(delim + 1).c_str(), "%x", &add_constant)) { |
| 313 BPLOG(ERROR) << "Failed to scan constant."; |
| 314 return false; |
| 315 } |
| 316 expression = expression.substr(0, delim); |
| 317 } |
| 318 |
| 319 // Set the the write address to the corresponding register. |
| 320 // TODO(liuandrew): Add support for partial registers, such as |
| 321 // the rax/eax/ax/ah/al chain. |
| 322 switch (context.GetContextCPU()) { |
| 323 case MD_CONTEXT_X86: |
| 324 if (!expression.compare("eax")) { |
| 325 *write_address = context.GetContextX86()->eax; |
| 326 } else if (!expression.compare("ebx")) { |
| 327 *write_address = context.GetContextX86()->ebx; |
| 328 } else if (!expression.compare("ecx")) { |
| 329 *write_address = context.GetContextX86()->ecx; |
| 330 } else if (!expression.compare("edx")) { |
| 331 *write_address = context.GetContextX86()->edx; |
| 332 } else if (!expression.compare("edi")) { |
| 333 *write_address = context.GetContextX86()->edi; |
| 334 } else if (!expression.compare("esi")) { |
| 335 *write_address = context.GetContextX86()->esi; |
| 336 } else if (!expression.compare("ebp")) { |
| 337 *write_address = context.GetContextX86()->ebp; |
| 338 } else if (!expression.compare("esp")) { |
| 339 *write_address = context.GetContextX86()->esp; |
| 340 } else if (!expression.compare("eip")) { |
| 341 *write_address = context.GetContextX86()->eip; |
| 342 } else { |
| 343 BPLOG(ERROR) << "Unsupported register"; |
| 344 return false; |
| 345 } |
| 346 break; |
| 347 case MD_CONTEXT_AMD64: |
| 348 if (!expression.compare("rax")) { |
| 349 *write_address = context.GetContextAMD64()->rax; |
| 350 } else if (!expression.compare("rbx")) { |
| 351 *write_address = context.GetContextAMD64()->rbx; |
| 352 } else if (!expression.compare("rcx")) { |
| 353 *write_address = context.GetContextAMD64()->rcx; |
| 354 } else if (!expression.compare("rdx")) { |
| 355 *write_address = context.GetContextAMD64()->rdx; |
| 356 } else if (!expression.compare("rdi")) { |
| 357 *write_address = context.GetContextAMD64()->rdi; |
| 358 } else if (!expression.compare("rsi")) { |
| 359 *write_address = context.GetContextAMD64()->rsi; |
| 360 } else if (!expression.compare("rbp")) { |
| 361 *write_address = context.GetContextAMD64()->rbp; |
| 362 } else if (!expression.compare("rsp")) { |
| 363 *write_address = context.GetContextAMD64()->rsp; |
| 364 } else if (!expression.compare("rip")) { |
| 365 *write_address = context.GetContextAMD64()->rip; |
| 366 } else if (!expression.compare("r8")) { |
| 367 *write_address = context.GetContextAMD64()->r8; |
| 368 } else if (!expression.compare("r9")) { |
| 369 *write_address = context.GetContextAMD64()->r9; |
| 370 } else if (!expression.compare("r10")) { |
| 371 *write_address = context.GetContextAMD64()->r10; |
| 372 } else if (!expression.compare("r11")) { |
| 373 *write_address = context.GetContextAMD64()->r11; |
| 374 } else if (!expression.compare("r12")) { |
| 375 *write_address = context.GetContextAMD64()->r12; |
| 376 } else if (!expression.compare("r13")) { |
| 377 *write_address = context.GetContextAMD64()->r13; |
| 378 } else if (!expression.compare("r14")) { |
| 379 *write_address = context.GetContextAMD64()->r14; |
| 380 } else if (!expression.compare("r15")) { |
| 381 *write_address = context.GetContextAMD64()->r15; |
| 382 } else { |
| 383 BPLOG(ERROR) << "Unsupported register"; |
| 384 return false; |
| 385 } |
| 386 break; |
| 387 default: |
| 388 // This should not occur since the same switch condition |
| 389 // should have terminated this method. |
| 390 return false; |
| 391 break; |
| 392 } |
| 393 |
| 394 // Add or subtract constant from write address (if applicable). |
| 395 *write_address = |
| 396 positive_add_constant ? |
| 397 *write_address + add_constant : *write_address - add_constant; |
| 398 |
| 399 return true; |
| 400 } |
| 401 |
| 402 bool ExploitabilityLinux::TokenizeObjdumpInstruction(const string &line, |
| 403 string *operation, |
| 404 string *dest, |
| 405 string *src) { |
| 406 if (!operation || !dest || !src) { |
| 407 BPLOG(ERROR) << "Null parameters passed."; |
| 408 return false; |
| 409 } |
| 410 |
| 411 // Set all pointer values to empty strings. |
| 412 *operation = ""; |
| 413 *dest = ""; |
| 414 *src = ""; |
| 415 |
| 416 // Tokenize the objdump line. |
| 417 vector<string> tokens; |
| 418 std::istringstream line_stream(line); |
| 419 copy(std::istream_iterator<string>(line_stream), |
| 420 std::istream_iterator<string>(), |
| 421 std::back_inserter(tokens)); |
| 422 |
| 423 // Regex for the data in hex form. Each byte is two hex digits. |
| 424 regex_t regex; |
| 425 regcomp(®ex, "^[[:xdigit:]]{2}$", REG_EXTENDED | REG_NOSUB); |
| 426 |
| 427 // Find and set the location of the operator. The operator appears |
| 428 // directly after the chain of bytes that define the instruction. The |
| 429 // operands will be the last token, given that the instruction has operands. |
| 430 // If not, the operator is the last token. The loop skips the first token |
| 431 // because the first token is the instruction number (namely "0:"). |
| 432 string operands = ""; |
| 433 for (size_t i = 1; i < tokens.size(); i++) { |
| 434 // Check if current token no longer is in byte format. |
| 435 if (regexec(®ex, tokens[i].c_str(), 0, NULL, 0)) { |
| 436 // instruction = tokens[i]; |
| 437 *operation = tokens[i]; |
| 438 // If the operator is the last token, there are no operands. |
| 439 if (i != tokens.size() - 1) { |
| 440 operands = tokens[tokens.size() - 1]; |
| 441 } |
| 442 break; |
| 443 } |
| 444 } |
| 445 regfree(®ex); |
| 446 |
| 447 if (operation->empty()) { |
| 448 BPLOG(ERROR) << "Failed to parse out operation from objdump instruction."; |
| 449 return false; |
| 450 } |
| 451 |
| 452 // Split operands into source and destination (if applicable). |
| 453 if (!operands.empty()) { |
| 454 size_t delim = operands.find(','); |
| 455 if (delim == string::npos) { |
| 456 *dest = operands; |
| 457 } else { |
| 458 *dest = operands.substr(0, delim); |
| 459 *src = operands.substr(delim + 1); |
| 460 } |
| 461 } |
| 462 return true; |
| 463 } |
| 464 |
| 465 bool ExploitabilityLinux::DisassembleBytes(const string &architecture, |
| 466 const uint8_t *raw_bytes, |
| 467 const unsigned int buffer_len, |
| 468 char *objdump_output_buffer) { |
| 469 if (!raw_bytes || !objdump_output_buffer) { |
| 470 BPLOG(ERROR) << "Bad input parameters."; |
| 471 return false; |
| 472 } |
| 473 |
| 474 // Write raw bytes around instruction pointer to a temporary file to |
| 475 // pass as an argument to objdump. |
| 476 char raw_bytes_tmpfile[] = "/tmp/breakpad_mem_region-raw_bytes-XXXXXX"; |
| 477 int raw_bytes_fd = mkstemp(raw_bytes_tmpfile); |
| 478 if (raw_bytes_fd < 0) { |
| 479 BPLOG(ERROR) << "Failed to create tempfile."; |
| 480 unlink(raw_bytes_tmpfile); |
| 481 return false; |
| 482 } |
| 483 if (write(raw_bytes_fd, raw_bytes, MAX_INSTRUCTION_LEN) |
| 484 != MAX_INSTRUCTION_LEN) { |
| 485 BPLOG(ERROR) << "Writing of raw bytes failed."; |
| 486 unlink(raw_bytes_tmpfile); |
| 487 return false; |
| 488 } |
| 489 |
| 490 char cmd[1024] = {0}; |
| 491 snprintf(cmd, |
| 492 1024, |
| 493 "objdump -D -b binary -M intel -m %s %s", |
| 494 architecture.c_str(), |
| 495 raw_bytes_tmpfile); |
| 496 FILE *objdump_fp = popen(cmd, "r"); |
| 497 if (!objdump_fp) { |
| 498 fclose(objdump_fp); |
| 499 unlink(raw_bytes_tmpfile); |
| 500 BPLOG(ERROR) << "Failed to call objdump."; |
| 501 return false; |
| 502 } |
| 503 if (fread(objdump_output_buffer, 1, buffer_len, objdump_fp) <= 0) { |
| 504 fclose(objdump_fp); |
| 505 unlink(raw_bytes_tmpfile); |
| 506 BPLOG(ERROR) << "Failed to read objdump output."; |
| 507 return false; |
| 508 } |
| 509 fclose(objdump_fp); |
| 510 unlink(raw_bytes_tmpfile); |
| 511 return true; |
| 512 } |
| 513 #endif // _WIN32 |
| 514 |
137 bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) { | 515 bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) { |
138 MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); | 516 MinidumpLinuxMapsList *linux_maps_list = dump_->GetLinuxMapsList(); |
139 // Inconclusive if there are no mappings available. | 517 // Inconclusive if there are no mappings available. |
140 if (!linux_maps_list) { | 518 if (!linux_maps_list) { |
141 return false; | 519 return false; |
142 } | 520 } |
143 const MinidumpLinuxMaps *linux_maps = | 521 const MinidumpLinuxMaps *linux_maps = |
144 linux_maps_list->GetLinuxMapsForAddress(stack_ptr); | 522 linux_maps_list->GetLinuxMapsForAddress(stack_ptr); |
145 // Checks if the stack pointer maps to a valid mapping and if the mapping | 523 // 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 | 524 // is not the stack. If the mapping has no name, it is inconclusive whether |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: | 595 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: |
218 return true; | 596 return true; |
219 break; | 597 break; |
220 default: | 598 default: |
221 return false; | 599 return false; |
222 break; | 600 break; |
223 } | 601 } |
224 } | 602 } |
225 | 603 |
226 } // namespace google_breakpad | 604 } // namespace google_breakpad |
OLD | NEW |