Chromium Code Reviews| 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 // Getting the instruction pointer. | 117 // Getting the instruction pointer. |
| 113 if (!context->GetInstructionPointer(&instruction_ptr)) { | 118 if (!context->GetInstructionPointer(&instruction_ptr)) { |
| 114 return EXPLOITABILITY_ERR_PROCESSING; | 119 return EXPLOITABILITY_ERR_PROCESSING; |
| 115 } | 120 } |
| 116 | 121 |
| 117 // Checking for the instruction pointer in a valid instruction region. | 122 // Checking for the instruction pointer in a valid instruction region. |
| 118 if (!this->InstructionPointerInCode(instruction_ptr)) { | 123 if (!this->InstructionPointerInCode(instruction_ptr)) { |
| 119 return EXPLOITABILITY_HIGH; | 124 return EXPLOITABILITY_HIGH; |
| 120 } | 125 } |
| 121 | 126 |
| 127 // There was no strong evidence suggesting exploitability, but the minidump | |
| 128 // does not appear totally benign either. | |
| 122 return EXPLOITABILITY_INTERESTING; | 129 return EXPLOITABILITY_INTERESTING; |
| 123 } | 130 } |
| 124 | 131 |
| 132 bool ExploitabilityLinux::Architecture32Bit() { | |
| 133 // GetContextCPU() should have already been successfully called before | |
|
ahonig
2015/07/14 19:32:18
Add a check to make sure this is the case. If the
liuandrew
2015/07/15 21:50:14
Done.
| |
| 134 // calling this method. Thus the switch statement should not seg fault. | |
| 135 switch (dump_->GetException()->GetContext()->GetContextCPU()) { | |
| 136 case MD_CONTEXT_ARM: | |
| 137 case MD_CONTEXT_X86: | |
| 138 return true; | |
| 139 case MD_CONTEXT_ARM64: | |
| 140 case MD_CONTEXT_AMD64: | |
| 141 return false; | |
| 142 default: | |
| 143 // This should not happen. The four architectures above should be | |
| 144 // the only Linux architectures. | |
| 145 BPLOG(INFO) << "Unsupported architecture."; | |
|
ivanpe
2015/07/14 00:23:02
Instead of bool, you should consider returning an
liuandrew
2015/07/15 21:50:14
Done.
| |
| 146 return false; | |
| 147 } | |
| 148 } | |
| 149 | |
| 125 bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) { | 150 bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) { |
| 126 // Here we get memory mapping. Most minidumps will not contain a memory | 151 // Here we get memory mapping. Most minidumps will not contain a memory |
| 127 // mapping, so we will commonly resort to checking modules. | 152 // mapping, so we will commonly resort to checking modules. |
| 128 MinidumpMemoryInfoList *mem_info_list = dump_->GetMemoryInfoList(); | 153 MinidumpMemoryInfoList *mem_info_list = dump_->GetMemoryInfoList(); |
| 129 const MinidumpMemoryInfo *mem_info = | 154 const MinidumpMemoryInfo *mem_info = |
| 130 mem_info_list ? | 155 mem_info_list ? |
| 131 mem_info_list->GetMemoryInfoForAddress(instruction_ptr) : NULL; | 156 mem_info_list->GetMemoryInfoForAddress(instruction_ptr) : NULL; |
| 132 | 157 |
| 133 // Checking if the memory mapping at the instruction pointer is executable. | 158 // Checking if the memory mapping at the instruction pointer is executable. |
| 134 // If there is no memory mapping, we will use the modules as reference. | 159 // If there is no memory mapping, we will use the modules as reference. |
| 135 if (mem_info != NULL) { | 160 if (mem_info != NULL) { |
| 136 return mem_info->IsExecutable(); | 161 return mem_info->IsExecutable(); |
| 137 } | 162 } |
| 138 | 163 |
| 139 // If the memory mapping retrieval fails, we will check the modules | 164 // If the memory mapping retrieval fails, we will check the modules |
| 140 // to see if the instruction pointer is inside a module. | 165 // 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(); | 166 MinidumpModuleList *minidump_module_list = dump_->GetModuleList(); |
| 144 return !minidump_module_list || | 167 const MinidumpModule *minidump_module = |
| 145 minidump_module_list->GetModuleForAddress(instruction_ptr); | 168 minidump_module_list ? |
| 169 minidump_module_list->GetModuleForAddress(instruction_ptr) : NULL; | |
| 170 | |
| 171 // If the instruction pointer isn't in a module, we can return false. | |
|
ivanpe
2015/07/14 00:23:02
Please, don't use "we" in comments. Comments shoul
liuandrew
2015/07/15 21:50:14
Done.
| |
| 172 if (minidump_module == NULL) { | |
| 173 return false; | |
| 174 } | |
| 175 | |
| 176 // Get ELF header data from the instruction pointer's module. | |
| 177 const uint64_t base_address = minidump_module->base_address(); | |
| 178 MinidumpMemoryList *memory_list = dump_->GetMemoryList(); | |
| 179 MinidumpMemoryRegion *memory_region = | |
| 180 memory_list ? | |
| 181 memory_list->GetMemoryRegionForAddress(base_address) : NULL; | |
| 182 | |
| 183 // The minidump does not have the correct memory region. | |
| 184 // This returns true because even though there is no memory data available, | |
| 185 // the evidence so far suggests that the instruction pointer is not at a | |
| 186 // bad location. | |
| 187 if (memory_region == NULL) { | |
| 188 return true; | |
| 189 } | |
| 190 | |
| 191 // Examine ELF headers. Depending on the architecture, the size of the | |
| 192 // ELF headers can differ. | |
| 193 if (this->Architecture32Bit()) { | |
| 194 // Check if the ELF header is within the memory region. | |
| 195 if (memory_region->GetSize() < sizeof(Elf32_Phdr)) { | |
| 196 return false; | |
| 197 } | |
| 198 // Set 32-bit ELF header and program header table. | |
|
ivanpe
2015/07/14 00:23:02
Instead of "Set" maybe is s better to say "Load" o
liuandrew
2015/07/15 21:50:14
Done.
| |
| 199 Elf32_Ehdr *header = this->LoadElf32Header(memory_region, base_address); | |
|
ivanpe
2015/07/14 00:23:02
Do we need to worry about ownership transfer here?
liuandrew
2015/07/15 21:50:13
using scoped_ptr
| |
| 200 assert(header->e_phentsize == sizeof(Elf32_Phdr)); | |
|
ahonig
2015/07/14 19:32:18
assert is compiled out in non-debug builds. If th
liuandrew
2015/07/15 21:50:14
Done.
| |
| 201 // Check if the program header table is within the memory region. | |
| 202 if (memory_region->GetSize() < | |
| 203 header->e_phoff + (header->e_phentsize * header->e_phnum)) { | |
| 204 return false; | |
| 205 } | |
| 206 | |
| 207 Elf32_Phdr *program_headers = this->LoadElf32PHeader(memory_region, | |
| 208 base_address, | |
| 209 header->e_phoff, | |
| 210 header->e_phentsize, | |
| 211 header->e_phnum); | |
| 212 // Find correct program header that corresponds to the instruction pointer. | |
| 213 for (int i = 0; i < header->e_phnum; i++) { | |
| 214 Elf32_Phdr program_header = program_headers[i]; | |
|
ivanpe
2015/07/14 00:23:02
Please, use const Elf32_Phdr& to avoid copy
| |
| 215 // Check if instruction pointer lies within this program header's region. | |
| 216 if (program_header.p_vaddr >= instruction_ptr && | |
| 217 program_header.p_vaddr + program_header.p_memsz < instruction_ptr) { | |
|
ivanpe
2015/07/14 00:23:02
This check seems wrong to me. It can only be true
liuandrew
2015/07/15 21:50:14
My bad. I flipped the equality signs. Fixed and ad
| |
| 218 free(header); | |
|
ivanpe
2015/07/14 00:23:01
I noticed that you free header here but you don't
ahonig
2015/07/14 19:32:18
The pattern of free is pretty complicated and erro
liuandrew
2015/07/15 21:50:14
Done.
| |
| 219 free(program_headers); | |
| 220 // Return whether this program header region is executable. | |
| 221 return program_header.p_flags & 1; | |
|
ivanpe
2015/07/14 00:23:01
Instead of the literal 1, isn't there a constant t
liuandrew
2015/07/15 21:50:14
Done.
| |
| 222 } | |
| 223 } | |
| 224 free(header); | |
| 225 free(program_headers); | |
| 226 } else { | |
| 227 // Check if the ELF header is within the memory region. | |
| 228 if (memory_region->GetSize() < sizeof(Elf64_Phdr)) { | |
| 229 return false; | |
| 230 } | |
| 231 // Set 64-bit ELF header and program header table. | |
| 232 Elf64_Ehdr *header = this->LoadElf64Header(memory_region, base_address); | |
| 233 assert(header->e_phentsize == sizeof(Elf64_Phdr)); | |
| 234 // Check if the program header table is within the memory region. | |
| 235 if (memory_region->GetSize() < | |
| 236 header->e_phoff + (header->e_phentsize * header->e_phnum)) { | |
| 237 return false; | |
| 238 } | |
| 239 Elf64_Phdr *program_headers = this->LoadElf64PHeader(memory_region, | |
| 240 base_address, | |
| 241 header->e_phoff, | |
| 242 header->e_phentsize, | |
| 243 header->e_phnum); | |
| 244 // Find correct program header that corresponds to the instruction pointer. | |
| 245 for (int i = 0; i < header->e_phnum; i++) { | |
| 246 Elf64_Phdr program_header = program_headers[i]; | |
| 247 // Check if instruction pointer lies within this program header's region. | |
| 248 if (program_header.p_vaddr >= instruction_ptr && | |
| 249 program_header.p_vaddr + program_header.p_memsz < instruction_ptr) { | |
| 250 free(header); | |
| 251 free(program_headers); | |
| 252 // Return whether this program header region is executable. | |
| 253 return program_header.p_flags & 1; | |
| 254 } | |
| 255 } | |
| 256 free(header); | |
| 257 free(program_headers); | |
| 258 } | |
| 259 | |
| 260 // The instruction pointer was not in an area identified by the ELF headers. | |
| 261 return false; | |
| 262 } | |
| 263 | |
| 264 void *ExploitabilityLinux::LoadElfHeader(MinidumpMemoryRegion *memory, | |
|
ivanpe
2015/07/14 00:23:02
I would suggest replacing this with a template met
liuandrew
2015/07/15 21:50:14
Done.
| |
| 265 const uint64_t base_address, | |
| 266 size_t header_size) { | |
| 267 void *header = malloc(header_size); | |
| 268 // Copy over each byte. | |
| 269 for (size_t i = 0; i < header_size; i++) { | |
| 270 uint8_t my_byte = 0; | |
| 271 // Get the value at the memory address. | |
| 272 memory->GetMemoryAtAddress(base_address + i, &my_byte); | |
| 273 memcpy(reinterpret_cast<char *>(header) + i, &my_byte, sizeof(uint8_t)); | |
| 274 } | |
| 275 return header; | |
| 276 } | |
| 277 | |
| 278 Elf32_Ehdr *ExploitabilityLinux::LoadElf32Header(MinidumpMemoryRegion *memory, | |
| 279 const uint64_t base_address) { | |
| 280 return reinterpret_cast<Elf32_Ehdr *>(LoadElfHeader(memory, | |
| 281 base_address, | |
| 282 sizeof(Elf32_Ehdr))); | |
| 283 } | |
| 284 | |
| 285 Elf64_Ehdr *ExploitabilityLinux::LoadElf64Header(MinidumpMemoryRegion *memory, | |
| 286 const uint64_t base_address) { | |
| 287 return reinterpret_cast<Elf64_Ehdr *>(LoadElfHeader(memory, | |
| 288 base_address, | |
| 289 sizeof(Elf64_Ehdr))); | |
| 290 } | |
| 291 | |
| 292 Elf32_Phdr *ExploitabilityLinux::LoadElf32PHeader(MinidumpMemoryRegion *memory, | |
| 293 const uint64_t base_address, | |
| 294 const uint64_t e_phoff, | |
| 295 const uint16_t e_phentsize, | |
| 296 const uint16_t e_phnum) { | |
| 297 // The base address with the offset makes the starting memory address. | |
| 298 // The entry size multiplied by the number of entries is the number of bytes. | |
| 299 return reinterpret_cast<Elf32_Phdr *>(LoadElfHeader(memory, | |
| 300 (base_address + e_phoff), | |
| 301 (e_phentsize * e_phnum))); | |
| 302 } | |
| 303 | |
| 304 Elf64_Phdr *ExploitabilityLinux::LoadElf64PHeader(MinidumpMemoryRegion *memory, | |
| 305 const uint64_t base_address, | |
| 306 const uint64_t e_phoff, | |
| 307 const uint16_t e_phentsize, | |
| 308 const uint16_t e_phnum) { | |
| 309 // The base address with the offset makes the starting memory address. | |
| 310 // The entry size multiplied by the number of entries is the number of bytes. | |
| 311 return reinterpret_cast<Elf64_Phdr *>(LoadElfHeader(memory, | |
| 312 (base_address + e_phoff), | |
| 313 (e_phentsize * e_phnum))); | |
| 146 } | 314 } |
| 147 | 315 |
| 148 bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream | 316 bool ExploitabilityLinux::BenignCrashTrigger(const MDRawExceptionStream |
| 149 *raw_exception_stream) { | 317 *raw_exception_stream) { |
| 150 // Here we check the cause of crash. | 318 // Here we check the cause of crash. |
| 151 // If the exception of the crash is a benign exception, | 319 // If the exception of the crash is a benign exception, |
| 152 // it is probably not exploitable. | 320 // it is probably not exploitable. |
| 153 switch (raw_exception_stream->exception_record.exception_code) { | 321 switch (raw_exception_stream->exception_record.exception_code) { |
| 154 case MD_EXCEPTION_CODE_LIN_SIGHUP: | 322 case MD_EXCEPTION_CODE_LIN_SIGHUP: |
| 155 case MD_EXCEPTION_CODE_LIN_SIGINT: | 323 case MD_EXCEPTION_CODE_LIN_SIGINT: |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 181 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: | 349 case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: |
| 182 return true; | 350 return true; |
| 183 break; | 351 break; |
| 184 default: | 352 default: |
| 185 return false; | 353 return false; |
| 186 break; | 354 break; |
| 187 } | 355 } |
| 188 } | 356 } |
| 189 | 357 |
| 190 } // namespace google_breakpad | 358 } // namespace google_breakpad |
| OLD | NEW |