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