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 #include <assert.h> | |
40 #include <elf.h> | |
41 #include <stdlib.h> | |
42 #include <string.h> | |
43 | |
39 #include "google_breakpad/common/minidump_exception_linux.h" | 44 #include "google_breakpad/common/minidump_exception_linux.h" |
40 #include "google_breakpad/processor/call_stack.h" | 45 #include "google_breakpad/processor/call_stack.h" |
41 #include "google_breakpad/processor/process_state.h" | 46 #include "google_breakpad/processor/process_state.h" |
42 #include "google_breakpad/processor/stack_frame.h" | 47 #include "google_breakpad/processor/stack_frame.h" |
43 #include "processor/logging.h" | 48 #include "processor/logging.h" |
44 | 49 |
45 namespace { | 50 namespace { |
46 | 51 |
47 // This function in libc is called if the program was compiled with | 52 // This function in libc is called if the program was compiled with |
48 // -fstack-protector and a function's stack canary changes. | 53 // -fstack-protector and a function's stack canary changes. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
102 // Check if the instruction pointer is in a valid instruction region | 107 // Check if the instruction pointer is in a valid instruction region |
103 // by finding if it maps to an executable part of memory. | 108 // by finding if it maps to an executable part of memory. |
104 uint64_t instruction_ptr = 0; | 109 uint64_t instruction_ptr = 0; |
105 | 110 |
106 const MinidumpContext *context = exception->GetContext(); | 111 const MinidumpContext *context = exception->GetContext(); |
107 if (context == NULL) { | 112 if (context == NULL) { |
108 BPLOG(INFO) << "No exception context."; | 113 BPLOG(INFO) << "No exception context."; |
109 return EXPLOITABILITY_ERR_PROCESSING; | 114 return EXPLOITABILITY_ERR_PROCESSING; |
110 } | 115 } |
111 | 116 |
117 if (this->ArchitectureType() == UNSUPPORTED_ARCHITECTURE) { | |
118 BPLOG(INFO) << "Unsupported architecture."; | |
119 return EXPLOITABILITY_ERR_PROCESSING; | |
120 } | |
112 // Getting the instruction pointer. | 121 // Getting the instruction pointer. |
113 if (!context->GetInstructionPointer(&instruction_ptr)) { | 122 if (!context->GetInstructionPointer(&instruction_ptr)) { |
123 BPLOG(INFO) << "Failed to retrieve instruction pointer."; | |
114 return EXPLOITABILITY_ERR_PROCESSING; | 124 return EXPLOITABILITY_ERR_PROCESSING; |
115 } | 125 } |
116 | 126 |
117 // Checking for the instruction pointer in a valid instruction region. | 127 // Checking for the instruction pointer in a valid instruction region. |
118 if (!this->InstructionPointerInCode(instruction_ptr)) { | 128 if (!this->InstructionPointerInCode(instruction_ptr)) { |
119 return EXPLOITABILITY_HIGH; | 129 return EXPLOITABILITY_HIGH; |
120 } | 130 } |
121 | 131 |
132 // There was no strong evidence suggesting exploitability, but the minidump | |
133 // does not appear totally benign either. | |
122 return EXPLOITABILITY_INTERESTING; | 134 return EXPLOITABILITY_INTERESTING; |
123 } | 135 } |
124 | 136 |
137 LinuxArchitectureType ExploitabilityLinux::ArchitectureType() { | |
138 // GetContextCPU() should have already been successfully called before | |
139 // calling this method. Thus there should be a raw exception stream for | |
140 // the minidump. | |
141 MinidumpException *exception = dump_->GetException(); | |
142 const DumpContext *dump_context = | |
143 exception ? | |
144 exception->GetContext() : NULL; | |
145 if (dump_context == NULL) { | |
146 BPLOG(INFO) << "No raw dump context."; | |
147 return UNSUPPORTED_ARCHITECTURE; | |
148 } | |
149 | |
150 // Check the architecture type. | |
151 switch (dump_context->GetContextCPU()) { | |
152 case MD_CONTEXT_ARM: | |
153 case MD_CONTEXT_X86: | |
154 return LINUX_32_BIT; | |
155 case MD_CONTEXT_ARM64: | |
156 case MD_CONTEXT_AMD64: | |
157 return LINUX_64_BIT; | |
158 default: | |
159 // This should not happen. The four architectures above should be | |
160 // the only Linux architectures. | |
161 BPLOG(INFO) << "Unsupported architecture."; | |
162 return UNSUPPORTED_ARCHITECTURE; | |
163 } | |
164 } | |
165 | |
125 bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) { | 166 bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) { |
126 // Here we get memory mapping. Most minidumps will not contain a memory | 167 // Get memory mapping. Most minidumps will not contain a memory |
127 // mapping, so we will commonly resort to checking modules. | 168 // mapping, so processing will commonly resort to checking modules. |
128 MinidumpMemoryInfoList *mem_info_list = dump_->GetMemoryInfoList(); | 169 MinidumpMemoryInfoList *mem_info_list = dump_->GetMemoryInfoList(); |
129 const MinidumpMemoryInfo *mem_info = | 170 const MinidumpMemoryInfo *mem_info = |
130 mem_info_list ? | 171 mem_info_list ? |
131 mem_info_list->GetMemoryInfoForAddress(instruction_ptr) : NULL; | 172 mem_info_list->GetMemoryInfoForAddress(instruction_ptr) : NULL; |
132 | 173 |
133 // Checking if the memory mapping at the instruction pointer is executable. | 174 // Check if the memory mapping at the instruction pointer is executable. |
134 // If there is no memory mapping, we will use the modules as reference. | 175 // If there is no memory mapping, processing will use the modules as reference . |
135 if (mem_info != NULL) { | 176 if (mem_info != NULL) { |
136 return mem_info->IsExecutable(); | 177 return mem_info->IsExecutable(); |
137 } | 178 } |
138 | 179 |
139 // If the memory mapping retrieval fails, we will check the modules | 180 // If the memory mapping retrieval fails, check the modules |
140 // to see if the instruction pointer is inside a module. | 181 // to see if the instruction pointer is inside a module. |
141 // TODO(liuandrew): Check if the instruction pointer lies in an executable | |
142 // region within the module. | |
143 MinidumpModuleList *minidump_module_list = dump_->GetModuleList(); | 182 MinidumpModuleList *minidump_module_list = dump_->GetModuleList(); |
144 return !minidump_module_list || | 183 const MinidumpModule *minidump_module = |
145 minidump_module_list->GetModuleForAddress(instruction_ptr); | 184 minidump_module_list ? |
185 minidump_module_list->GetModuleForAddress(instruction_ptr) : NULL; | |
186 | |
187 // If the instruction pointer isn't in a module, return false. | |
188 if (minidump_module == NULL) { | |
189 return false; | |
190 } | |
191 | |
192 // Get ELF header data from the instruction pointer's module. | |
193 const uint64_t base_address = minidump_module->base_address(); | |
194 MinidumpMemoryList *memory_list = dump_->GetMemoryList(); | |
195 MinidumpMemoryRegion *memory_region = | |
196 memory_list ? | |
197 memory_list->GetMemoryRegionForAddress(base_address) : NULL; | |
198 | |
199 // The minidump does not have the correct memory region. | |
200 // This returns true because even though there is no memory data available, | |
201 // the evidence so far suggests that the instruction pointer is not at a | |
202 // bad location. | |
203 if (memory_region == NULL) { | |
204 return true; | |
205 } | |
206 | |
207 // Examine ELF headers. Depending on the architecture, the size of the | |
208 // ELF headers can differ. | |
209 LinuxArchitectureType architecture = this->ArchitectureType(); | |
210 if (architecture == LINUX_32_BIT) { | |
211 // Check if the ELF header is within the memory region and if the | |
212 // instruction pointer lies within the ELF header. | |
213 if (memory_region->GetSize() < sizeof(Elf32_Ehdr) || | |
214 instruction_ptr < base_address + sizeof(Elf32_Ehdr)) { | |
215 return false; | |
216 } | |
217 // Load 32-bit ELF header. | |
218 scoped_ptr<Elf32_Ehdr> header(new Elf32_Ehdr); | |
ivanpe
2015/07/16 00:45:23
Is this header needed only in the scope of this me
liuandrew
2015/07/16 17:07:02
Done.
You're right; it is only needed within the
| |
219 this->LoadElfHeader(header, memory_region, base_address); | |
220 // Check if the program header table is within the memory region, and | |
221 // validate that the program header entry size is correct. | |
222 if (header->e_phentsize != sizeof(Elf32_Phdr) || | |
223 memory_region->GetSize() < | |
224 header->e_phoff + (header->e_phentsize * header->e_phnum)) { | |
225 return false; | |
226 } | |
227 // Load 32-bit Program Header Table. | |
228 scoped_array<Elf32_Phdr> program_headers(new Elf32_Phdr[header->e_phnum]); | |
229 this->LoadElfHeaderTable(program_headers, | |
230 memory_region, | |
231 base_address + header->e_phoff, | |
232 header->e_phnum); | |
233 // Find correct program header that corresponds to the instruction pointer. | |
234 for (int i = 0; i < header->e_phnum; i++) { | |
235 const Elf32_Phdr& program_header = program_headers[i]; | |
236 // Check if instruction pointer lies within this program header's region. | |
237 if (instruction_ptr >= program_header.p_vaddr && | |
238 instruction_ptr < program_header.p_vaddr + program_header.p_memsz) { | |
239 // Return whether this program header region is executable. | |
240 return program_header.p_flags & PF_X; | |
241 } | |
242 } | |
243 } else if (architecture == LINUX_64_BIT) { | |
244 // Check if the ELF header is within the memory region and if the | |
245 // instruction pointer lies within the ELF header. | |
246 if (memory_region->GetSize() < sizeof(Elf64_Ehdr) || | |
247 instruction_ptr < base_address + sizeof(Elf64_Ehdr)) { | |
248 return false; | |
249 } | |
250 // Load 64-bit ELF header. | |
251 scoped_ptr<Elf64_Ehdr> header(new Elf64_Ehdr); | |
ivanpe
2015/07/16 00:45:23
Elf64_Ehdr header;
liuandrew
2015/07/16 17:07:02
Done.
| |
252 this->LoadElfHeader(header, memory_region, base_address); | |
253 // Check if the program header table is within the memory region, and | |
254 // validate that the program header entry size is correct. | |
255 if (header->e_phentsize != sizeof(Elf64_Phdr) || | |
256 memory_region->GetSize() < | |
257 header->e_phoff + (header->e_phentsize * header->e_phnum)) { | |
258 return false; | |
259 } | |
260 // Load 64-bit Program Header Table. | |
261 scoped_array<Elf64_Phdr> program_headers(new Elf64_Phdr[header->e_phnum]); | |
262 this->LoadElfHeaderTable(program_headers, | |
263 memory_region, | |
264 base_address + header->e_phoff, | |
265 header->e_phnum); | |
266 // Find correct program header that corresponds to the instruction pointer. | |
267 for (int i = 0; i < header->e_phnum; i++) { | |
268 const Elf64_Phdr& program_header = program_headers[i]; | |
269 // Check if instruction pointer lies within this program header's region. | |
270 if (instruction_ptr >= program_header.p_vaddr && | |
271 instruction_ptr < program_header.p_vaddr + program_header.p_memsz) { | |
272 // Return whether this program header region is executable. | |
273 return program_header.p_flags & PF_X; | |
274 } | |
275 } | |
276 } | |
277 | |
278 // The instruction pointer was not in an area identified by the ELF headers. | |
279 return false; | |
146 } | 280 } |
147 | 281 |
148 bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream | 282 bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream |
149 *raw_exception_stream) { | 283 *raw_exception_stream) { |
150 // Here we check the cause of crash. | 284 // Check the cause of crash. |
151 // If the exception of the crash is a benign exception, | 285 // If the exception of the crash is a benign exception, |
152 // it is probably not exploitable. | 286 // it is probably not exploitable. |
153 switch (raw_exception_stream->exception_record.exception_code) { | 287 switch (raw_exception_stream->exception_record.exception_code) { |
154 case MD_EXCEPTION_CODE_LIN_SIGHUP: | 288 case MD_EXCEPTION_CODE_LIN_SIGHUP: |
155 case MD_EXCEPTION_CODE_LIN_SIGINT: | 289 case MD_EXCEPTION_CODE_LIN_SIGINT: |
156 case MD_EXCEPTION_CODE_LIN_SIGQUIT: | 290 case MD_EXCEPTION_CODE_LIN_SIGQUIT: |
157 case MD_EXCEPTION_CODE_LIN_SIGTRAP: | 291 case MD_EXCEPTION_CODE_LIN_SIGTRAP: |
158 case MD_EXCEPTION_CODE_LIN_SIGABRT: | 292 case MD_EXCEPTION_CODE_LIN_SIGABRT: |
159 case MD_EXCEPTION_CODE_LIN_SIGFPE: | 293 case MD_EXCEPTION_CODE_LIN_SIGFPE: |
160 case MD_EXCEPTION_CODE_LIN_SIGKILL: | 294 case MD_EXCEPTION_CODE_LIN_SIGKILL: |
(...skipping 20 matching lines...) Expand all Loading... | |
181 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: | 315 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: |
182 return true; | 316 return true; |
183 break; | 317 break; |
184 default: | 318 default: |
185 return false; | 319 return false; |
186 break; | 320 break; |
187 } | 321 } |
188 } | 322 } |
189 | 323 |
190 } // namespace google_breakpad | 324 } // namespace google_breakpad |
OLD | NEW |