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