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