Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010, Google Inc. | 1 // Copyright (c) 2010, 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 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 81 return my_strncmp(mapping.name, | 81 return my_strncmp(mapping.name, |
| 82 kMappedFileUnsafePrefix, | 82 kMappedFileUnsafePrefix, |
| 83 sizeof(kMappedFileUnsafePrefix) - 1) == 0; | 83 sizeof(kMappedFileUnsafePrefix) - 1) == 0; |
| 84 } | 84 } |
| 85 | 85 |
| 86 namespace google_breakpad { | 86 namespace google_breakpad { |
| 87 | 87 |
| 88 // All interesting auvx entry types are below AT_SYSINFO_EHDR | 88 // All interesting auvx entry types are below AT_SYSINFO_EHDR |
| 89 #define AT_MAX AT_SYSINFO_EHDR | 89 #define AT_MAX AT_SYSINFO_EHDR |
| 90 | 90 |
| 91 LinuxDumper::LinuxDumper(pid_t pid) | 91 LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) |
| 92 : pid_(pid), | 92 : pid_(pid), |
| 93 root_prefix_(root_prefix), | |
| 93 crash_address_(0), | 94 crash_address_(0), |
| 94 crash_signal_(0), | 95 crash_signal_(0), |
| 95 crash_thread_(pid), | 96 crash_thread_(pid), |
| 96 threads_(&allocator_, 8), | 97 threads_(&allocator_, 8), |
| 97 mappings_(&allocator_), | 98 mappings_(&allocator_), |
| 98 auxv_(&allocator_, AT_MAX + 1) { | 99 auxv_(&allocator_, AT_MAX + 1) { |
| 100 assert(root_prefix_); | |
|
Luis Héctor Chávez
2016/03/03 22:10:43
Maybe also assert that my_strlen(root_prefix) < PA
Dominik Laskowski
2016/03/04 01:03:15
Done.
| |
| 99 // The passed-in size to the constructor (above) is only a hint. | 101 // The passed-in size to the constructor (above) is only a hint. |
| 100 // Must call .resize() to do actual initialization of the elements. | 102 // Must call .resize() to do actual initialization of the elements. |
| 101 auxv_.resize(AT_MAX + 1); | 103 auxv_.resize(AT_MAX + 1); |
| 102 } | 104 } |
| 103 | 105 |
| 104 LinuxDumper::~LinuxDumper() { | 106 LinuxDumper::~LinuxDumper() { |
| 105 } | 107 } |
| 106 | 108 |
| 107 bool LinuxDumper::Init() { | 109 bool LinuxDumper::Init() { |
| 108 return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); | 110 return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 132 linux_gate = reinterpret_cast<void*>(mapping.start_addr); | 134 linux_gate = reinterpret_cast<void*>(mapping.start_addr); |
| 133 } else { | 135 } else { |
| 134 linux_gate = allocator_.Alloc(mapping.size); | 136 linux_gate = allocator_.Alloc(mapping.size); |
| 135 CopyFromProcess(linux_gate, pid_, | 137 CopyFromProcess(linux_gate, pid_, |
| 136 reinterpret_cast<const void*>(mapping.start_addr), | 138 reinterpret_cast<const void*>(mapping.start_addr), |
| 137 mapping.size); | 139 mapping.size); |
| 138 } | 140 } |
| 139 return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); | 141 return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); |
| 140 } | 142 } |
| 141 | 143 |
| 142 char filename[NAME_MAX]; | 144 char filename[PATH_MAX]; |
| 143 size_t filename_len = my_strlen(mapping.name); | 145 if (!GetMappingAbsolutePath(mapping, filename)) |
|
Luis Héctor Chávez
2016/03/03 22:10:43
This used to assert if the mapping name exceeded N
Dominik Laskowski
2016/03/04 01:03:15
Done.
vapier
2016/03/07 16:32:36
i don't think keeping that assert adds anything us
| |
| 144 if (filename_len >= NAME_MAX) { | |
| 145 assert(false); | |
| 146 return false; | 146 return false; |
| 147 } | |
| 148 my_memcpy(filename, mapping.name, filename_len); | |
| 149 filename[filename_len] = '\0'; | |
| 150 bool filename_modified = HandleDeletedFileInMapping(filename); | 147 bool filename_modified = HandleDeletedFileInMapping(filename); |
| 151 | 148 |
| 152 MemoryMappedFile mapped_file(filename, mapping.offset); | 149 MemoryMappedFile mapped_file(filename, mapping.offset); |
| 153 if (!mapped_file.data() || mapped_file.size() < SELFMAG) | 150 if (!mapped_file.data() || mapped_file.size() < SELFMAG) |
| 154 return false; | 151 return false; |
| 155 | 152 |
| 156 bool success = | 153 bool success = |
| 157 FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); | 154 FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); |
| 158 if (success && member && filename_modified) { | 155 if (success && member && filename_modified) { |
| 159 mappings_[mapping_id]->name[filename_len - | 156 mappings_[mapping_id]->name[my_strlen(mapping.name) - |
| 160 sizeof(kDeletedSuffix) + 1] = '\0'; | 157 sizeof(kDeletedSuffix) + 1] = '\0'; |
| 161 } | 158 } |
| 162 | 159 |
| 163 return success; | 160 return success; |
| 164 } | 161 } |
| 165 | 162 |
| 163 bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, | |
| 164 char path[PATH_MAX]) const { | |
| 165 return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && | |
| 166 my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX; | |
| 167 } | |
| 168 | |
| 166 namespace { | 169 namespace { |
| 167 bool ElfFileSoNameFromMappedFile( | 170 bool ElfFileSoNameFromMappedFile( |
| 168 const void* elf_base, char* soname, size_t soname_size) { | 171 const void* elf_base, char* soname, size_t soname_size) { |
| 169 if (!IsValidElf(elf_base)) { | 172 if (!IsValidElf(elf_base)) { |
| 170 // Not ELF | 173 // Not ELF |
| 171 return false; | 174 return false; |
| 172 } | 175 } |
| 173 | 176 |
| 174 const void* segment_start; | 177 const void* segment_start; |
| 175 size_t segment_size; | 178 size_t segment_size; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 205 } | 208 } |
| 206 | 209 |
| 207 // Did not find SONAME | 210 // Did not find SONAME |
| 208 return false; | 211 return false; |
| 209 } | 212 } |
| 210 | 213 |
| 211 // Find the shared object name (SONAME) by examining the ELF information | 214 // Find the shared object name (SONAME) by examining the ELF information |
| 212 // for |mapping|. If the SONAME is found copy it into the passed buffer | 215 // for |mapping|. If the SONAME is found copy it into the passed buffer |
| 213 // |soname| and return true. The size of the buffer is |soname_size|. | 216 // |soname| and return true. The size of the buffer is |soname_size|. |
| 214 // The SONAME will be truncated if it is too long to fit in the buffer. | 217 // The SONAME will be truncated if it is too long to fit in the buffer. |
| 215 bool ElfFileSoName( | 218 bool ElfFileSoName(const LinuxDumper& dumper, |
| 216 const MappingInfo& mapping, char* soname, size_t soname_size) { | 219 const MappingInfo& mapping, char* soname, size_t soname_size) { |
| 217 if (IsMappedFileOpenUnsafe(mapping)) { | 220 if (IsMappedFileOpenUnsafe(mapping)) { |
| 218 // Not safe | 221 // Not safe |
| 219 return false; | 222 return false; |
| 220 } | 223 } |
| 221 | 224 |
| 222 char filename[NAME_MAX]; | 225 char filename[PATH_MAX]; |
| 223 size_t filename_len = my_strlen(mapping.name); | 226 if (!dumper.GetMappingAbsolutePath(mapping, filename)) |
| 224 if (filename_len >= NAME_MAX) { | |
| 225 assert(false); | |
| 226 // name too long | |
| 227 return false; | 227 return false; |
| 228 } | |
| 229 | |
| 230 my_memcpy(filename, mapping.name, filename_len); | |
| 231 filename[filename_len] = '\0'; | |
| 232 | 228 |
| 233 MemoryMappedFile mapped_file(filename, mapping.offset); | 229 MemoryMappedFile mapped_file(filename, mapping.offset); |
| 234 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { | 230 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { |
| 235 // mmap failed | 231 // mmap failed |
| 236 return false; | 232 return false; |
| 237 } | 233 } |
| 238 | 234 |
| 239 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); | 235 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); |
| 240 } | 236 } |
| 241 | 237 |
| 242 } // namespace | 238 } // namespace |
| 243 | 239 |
| 244 | 240 |
| 245 // static | |
| 246 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, | 241 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, |
| 247 char* file_path, | 242 char* file_path, |
| 248 size_t file_path_size, | 243 size_t file_path_size, |
| 249 char* file_name, | 244 char* file_name, |
| 250 size_t file_name_size) { | 245 size_t file_name_size) { |
| 251 my_strlcpy(file_path, mapping.name, file_path_size); | 246 my_strlcpy(file_path, mapping.name, file_path_size); |
| 252 | 247 |
| 253 // If an executable is mapped from a non-zero offset, this is likely because | 248 // If an executable is mapped from a non-zero offset, this is likely because |
| 254 // the executable was loaded directly from inside an archive file (e.g., an | 249 // the executable was loaded directly from inside an archive file (e.g., an |
| 255 // apk on Android). We try to find the name of the shared object (SONAME) by | 250 // apk on Android). We try to find the name of the shared object (SONAME) by |
| 256 // looking in the file for ELF sections. | 251 // looking in the file for ELF sections. |
| 257 bool mapped_from_archive = false; | 252 bool mapped_from_archive = false; |
| 258 if (mapping.exec && mapping.offset != 0) | 253 if (mapping.exec && mapping.offset != 0) { |
| 259 mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size); | 254 mapped_from_archive = |
| 255 ElfFileSoName(*this, mapping, file_name, file_name_size); | |
| 256 } | |
| 260 | 257 |
| 261 if (mapped_from_archive) { | 258 if (mapped_from_archive) { |
| 262 // Some tools (e.g., stackwalk) extract the basename from the pathname. In | 259 // Some tools (e.g., stackwalk) extract the basename from the pathname. In |
| 263 // this case, we append the file_name to the mapped archive path as follows: | 260 // this case, we append the file_name to the mapped archive path as follows: |
| 264 // file_name := libname.so | 261 // file_name := libname.so |
| 265 // file_path := /path/to/ARCHIVE.APK/libname.so | 262 // file_path := /path/to/ARCHIVE.APK/libname.so |
| 266 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { | 263 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { |
| 267 my_strlcat(file_path, "/", file_path_size); | 264 my_strlcat(file_path, "/", file_path_size); |
| 268 my_strlcat(file_path, file_name, file_path_size); | 265 my_strlcat(file_path, file_name, file_path_size); |
| 269 } | 266 } |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 const size_t path_len = my_strlen(path); | 570 const size_t path_len = my_strlen(path); |
| 574 if (path_len < kDeletedSuffixLen + 2) | 571 if (path_len < kDeletedSuffixLen + 2) |
| 575 return false; | 572 return false; |
| 576 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, | 573 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, |
| 577 kDeletedSuffixLen) != 0) { | 574 kDeletedSuffixLen) != 0) { |
| 578 return false; | 575 return false; |
| 579 } | 576 } |
| 580 | 577 |
| 581 // Check |path| against the /proc/pid/exe 'symlink'. | 578 // Check |path| against the /proc/pid/exe 'symlink'. |
| 582 char exe_link[NAME_MAX]; | 579 char exe_link[NAME_MAX]; |
| 583 char new_path[NAME_MAX]; | |
| 584 if (!BuildProcPath(exe_link, pid_, "exe")) | 580 if (!BuildProcPath(exe_link, pid_, "exe")) |
| 585 return false; | 581 return false; |
| 586 if (!SafeReadLink(exe_link, new_path)) | 582 MappingInfo new_mapping = {0}; |
| 583 if (!SafeReadLink(exe_link, new_mapping.name)) | |
| 584 return false; | |
| 585 char new_path[PATH_MAX]; | |
| 586 if (!GetMappingAbsolutePath(new_mapping, new_path)) | |
| 587 return false; | 587 return false; |
| 588 if (my_strcmp(path, new_path) != 0) | 588 if (my_strcmp(path, new_path) != 0) |
| 589 return false; | 589 return false; |
| 590 | 590 |
| 591 // Check to see if someone actually named their executable 'foo (deleted)'. | 591 // Check to see if someone actually named their executable 'foo (deleted)'. |
| 592 struct kernel_stat exe_stat; | 592 struct kernel_stat exe_stat; |
| 593 struct kernel_stat new_path_stat; | 593 struct kernel_stat new_path_stat; |
| 594 if (sys_stat(exe_link, &exe_stat) == 0 && | 594 if (sys_stat(exe_link, &exe_stat) == 0 && |
| 595 sys_stat(new_path, &new_path_stat) == 0 && | 595 sys_stat(new_path, &new_path_stat) == 0 && |
| 596 exe_stat.st_dev == new_path_stat.st_dev && | 596 exe_stat.st_dev == new_path_stat.st_dev && |
| 597 exe_stat.st_ino == new_path_stat.st_ino) { | 597 exe_stat.st_ino == new_path_stat.st_ino) { |
| 598 return false; | 598 return false; |
| 599 } | 599 } |
| 600 | 600 |
| 601 my_memcpy(path, exe_link, NAME_MAX); | 601 my_memcpy(path, exe_link, NAME_MAX); |
| 602 return true; | 602 return true; |
| 603 } | 603 } |
| 604 | 604 |
| 605 } // namespace google_breakpad | 605 } // namespace google_breakpad |
| OLD | NEW |