1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "crash_collector" 18 19 #include "coredump_writer.h" 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <sys/statvfs.h> 24 #include <unistd.h> 25 26 #include <android-base/file.h> 27 #include <android-base/unique_fd.h> 28 #include <log/log.h> 29 30 // From external/google-breakpad. 31 #include "common/linux/elf_core_dump.h" 32 33 namespace { 34 35 const size_t kMaxCoredumpSize = 256 * 1024 * 1024; 36 37 int64_t GetFreeDiskSpace(const std::string& path) { 38 struct statvfs stats; 39 if (TEMP_FAILURE_RETRY(statvfs(path.c_str(), &stats)) != 0) { 40 ALOGE("statvfs() failed. errno = %d", errno); 41 return -1; 42 } 43 return static_cast<int64_t>(stats.f_bavail) * stats.f_frsize; 44 } 45 46 bool Seek(int fd, off_t offset) { 47 return lseek(fd, offset, SEEK_SET) == offset; 48 } 49 50 template<typename T> 51 T GetValueFromNote(const google_breakpad::ElfCoreDump::Note& note, 52 size_t offset, 53 T default_value) { 54 const T* p = note.GetDescription().GetData<T>(offset); 55 return p ? *p : default_value; 56 } 57 58 } // namespace 59 60 class CoredumpWriter::FdReader { 61 public: 62 explicit FdReader(int fd) : fd_(fd), bytes_read_(0) {} 63 64 // Reads the given number of bytes. 65 bool Read(void* buf, size_t num_bytes) { 66 if (!android::base::ReadFully(fd_, buf, num_bytes)) 67 return false; 68 bytes_read_ += num_bytes; 69 return true; 70 } 71 72 // Reads the given number of bytes and writes it to fd_dest. 73 bool CopyTo(int fd_dest, size_t num_bytes) { 74 const size_t kBufSize = 32768; 75 char buf[kBufSize]; 76 while (num_bytes > 0) { 77 int rv = TEMP_FAILURE_RETRY( 78 read(fd_, buf, std::min(kBufSize, num_bytes))); 79 if (rv == 0) 80 break; 81 if (rv == -1) 82 return false; 83 if (fd_dest != -1 && !android::base::WriteFully(fd_dest, buf, rv)) 84 return false; 85 num_bytes -= rv; 86 bytes_read_ += rv; 87 } 88 return num_bytes == 0; 89 } 90 91 // Reads data and discards it to get to the specified position. 92 bool Seek(size_t offset) { 93 if (offset < bytes_read_) // Cannot move backward. 94 return false; 95 return CopyTo(-1, offset - bytes_read_); 96 } 97 98 private: 99 int fd_; 100 size_t bytes_read_; 101 102 DISALLOW_COPY_AND_ASSIGN(FdReader); 103 }; 104 105 CoredumpWriter::CoredumpWriter(int fd_src, 106 const std::string& coredump_filename, 107 const std::string& proc_files_dir) 108 : fd_src_(fd_src), 109 coredump_filename_(coredump_filename), 110 proc_files_dir_(proc_files_dir) { 111 } 112 113 CoredumpWriter::~CoredumpWriter() { 114 } 115 116 ssize_t CoredumpWriter::WriteCoredump() { 117 android::base::unique_fd fd_dest( 118 TEMP_FAILURE_RETRY(open(coredump_filename_.c_str(), 119 O_WRONLY | O_CREAT | O_EXCL, 120 S_IRUSR | S_IWUSR))); 121 if (fd_dest == -1) { 122 ALOGE("Failed to open: %s, errno = %d", coredump_filename_.c_str(), errno); 123 return -1; 124 } 125 ssize_t result = WriteCoredumpToFD(fd_dest); 126 fd_dest.reset(-1); 127 if (result == -1) 128 unlink(coredump_filename_.c_str()); 129 return result; 130 } 131 132 ssize_t CoredumpWriter::WriteCoredumpToFD(int fd_dest) { 133 // Input coredump is generated by kernel's fs/binfmt_elf.c and formatted like: 134 // 135 // ELF Header 136 // Program Header 1 137 // Program Header 2 138 // ... 139 // Program Header n 140 // Segment 1 (This segment's type should be PT_NOTE) 141 // Segment 2 142 // ... 143 // Segment n 144 145 // First, read ELF Header, all program headers, and the first segment whose 146 // type is PT_NOTE. 147 FdReader reader(fd_src_); 148 Ehdr elf_header; 149 std::vector<Phdr> program_headers; 150 std::vector<char> note_buf; 151 if (!ReadUntilNote(&reader, &elf_header, &program_headers, ¬e_buf)) { 152 return -1; 153 } 154 // Get a set of address ranges occupied by mapped files from NOTE. 155 FileMappings file_mappings; 156 if (!GetFileMappings(note_buf, &file_mappings)) { 157 return -1; 158 } 159 // Filter out segments backed by mapped files as they are useless when 160 // generating minidump. 161 std::vector<Phdr> program_headers_filtered; 162 FilterSegments(program_headers, file_mappings, &program_headers_filtered); 163 164 // Calculate the coredump size limit. 165 const int64_t free_disk_space = GetFreeDiskSpace(coredump_filename_); 166 if (free_disk_space < 0) { 167 return -1; 168 } 169 coredump_size_limit_ = std::min(static_cast<size_t>(free_disk_space / 20), 170 kMaxCoredumpSize); 171 172 // Calculate the output file size. 173 expected_coredump_size_ = program_headers_filtered.back().p_offset + 174 program_headers_filtered.back().p_filesz; 175 if (expected_coredump_size_ > coredump_size_limit_) { 176 ALOGE("Coredump too large: %zu", expected_coredump_size_); 177 return -1; 178 } 179 180 // Write proc files. 181 if (!WriteAuxv(note_buf, proc_files_dir_ + "/auxv") || 182 !WriteMaps(program_headers, file_mappings, proc_files_dir_ + "/maps")) { 183 return -1; 184 } 185 186 // Write ELF header. 187 if (!android::base::WriteFully(fd_dest, &elf_header, sizeof(elf_header))) { 188 ALOGE("Failed to write ELF header."); 189 return -1; 190 } 191 // Write program headers. 192 for (size_t i = 0; i < program_headers_filtered.size(); ++i) { 193 const Phdr& program_header = program_headers_filtered[i]; 194 const size_t offset = sizeof(elf_header) + i * elf_header.e_phentsize; 195 if (!Seek(fd_dest, offset) || 196 !android::base::WriteFully(fd_dest, &program_header, 197 sizeof(program_header))) { 198 ALOGE("Failed to write program header: i = %zu", i); 199 return -1; 200 } 201 } 202 // Write NOTE segment. 203 if (!Seek(fd_dest, program_headers_filtered[0].p_offset) || 204 !android::base::WriteFully(fd_dest, note_buf.data(), note_buf.size())) { 205 ALOGE("Failed to write NOTE."); 206 return -1; 207 } 208 // Read all remaining segments and write some of them. 209 for (size_t i = 1; i < program_headers_filtered.size(); ++i) { 210 const Phdr& program_header = program_headers_filtered[i]; 211 if (program_header.p_filesz > 0) { 212 const Phdr& program_header_original = program_headers[i]; 213 if (!reader.Seek(program_header_original.p_offset)) { 214 ALOGE("Failed to seek segment: i = %zu", i); 215 return -1; 216 } 217 if (!Seek(fd_dest, program_header.p_offset) || 218 !reader.CopyTo(fd_dest, program_header.p_filesz)) { 219 ALOGE("Failed to write segment: i = %zu", i); 220 return -1; 221 } 222 } 223 } 224 return expected_coredump_size_; 225 } 226 227 bool CoredumpWriter::ReadUntilNote(FdReader* reader, 228 Ehdr* elf_header, 229 std::vector<Phdr>* program_headers, 230 std::vector<char>* note_buf) { 231 // Read ELF header. 232 if (!reader->Read(elf_header, sizeof(*elf_header)) || 233 memcmp(elf_header->e_ident, ELFMAG, SELFMAG) != 0 || 234 elf_header->e_ident[EI_CLASS] != google_breakpad::ElfCoreDump::kClass || 235 elf_header->e_version != EV_CURRENT || 236 elf_header->e_type != ET_CORE || 237 elf_header->e_ehsize != sizeof(Ehdr) || 238 elf_header->e_phentsize != sizeof(Phdr)) { 239 ALOGE("Failed to read ELF header."); 240 return false; 241 } 242 243 // Read program headers; 244 program_headers->resize(elf_header->e_phnum); 245 if (!reader->Seek(elf_header->e_phoff) || 246 !reader->Read(program_headers->data(), 247 sizeof(Phdr) * program_headers->size())) { 248 ALOGE("Failed to read program headers."); 249 return false; 250 } 251 252 // The first segment should be NOTE. 253 if (program_headers->size() < 1 || 254 (*program_headers)[0].p_type != PT_NOTE) { 255 ALOGE("Failed to locate NOTE."); 256 return false; 257 } 258 const Phdr& note_program_header = (*program_headers)[0]; 259 260 // Read NOTE segment. 261 note_buf->resize(note_program_header.p_filesz); 262 if (!reader->Seek(note_program_header.p_offset) || 263 !reader->Read(note_buf->data(), note_buf->size())) { 264 ALOGE("Failed to read NOTE."); 265 return false; 266 } 267 return true; 268 } 269 270 bool CoredumpWriter::GetFileMappings(const std::vector<char>& note_buf, 271 FileMappings* file_mappings) { 272 // Locate FILE note. 273 google_breakpad::ElfCoreDump::Note note( 274 google_breakpad::MemoryRange(note_buf.data(), note_buf.size())); 275 while (note.IsValid() && note.GetType() != NT_FILE) { 276 note = note.GetNextNote(); 277 } 278 if (!note.IsValid()) { 279 ALOGE("Failed to locate NT_FILE."); 280 return false; 281 } 282 283 // NT_FILE note format: (see kernel's fs/binfmt_elf.c for details) 284 // Number of mapped files 285 // Page size 286 // Start address of file 1 287 // End address of file 1 288 // Offset of file 1 289 // Start address of file 2 290 // ... 291 // Offset of file n 292 // File name 1 (null-terminated) 293 // File name 2 294 // ... 295 // File name n 296 const long kInvalidValue = -1; 297 const long file_count = GetValueFromNote<long>(note, 0, kInvalidValue); 298 const long page_size = GetValueFromNote<long>(note, sizeof(long), 299 kInvalidValue); 300 if (file_count == kInvalidValue || page_size == kInvalidValue) { 301 ALOGE("Invalid FILE note."); 302 return false; 303 } 304 // Read contents of FILE note. 305 size_t filename_pos = sizeof(long) * (2 + 3 * file_count); 306 for (long i = 0; i < file_count; ++i) { 307 const long start = GetValueFromNote<long>( 308 note, sizeof(long) * (2 + 3 * i), kInvalidValue); 309 const long end = GetValueFromNote<long>( 310 note, sizeof(long) * (2 + 3 * i + 1), kInvalidValue); 311 const long offset = GetValueFromNote<long>( 312 note, sizeof(long) * (2 + 3 * i + 2), kInvalidValue); 313 if (start == kInvalidValue || end == kInvalidValue || 314 offset == kInvalidValue) { 315 ALOGE("Invalid FILE Note."); 316 return false; 317 } 318 // Add a new mapping. 319 FileInfo& info = (*file_mappings)[std::make_pair(start, end)]; 320 info.offset = offset * page_size; 321 // Read file name. 322 while (true) { 323 const char c = GetValueFromNote<char>(note, filename_pos++, 0); 324 if (!c) 325 break; 326 info.path.push_back(c); 327 } 328 } 329 return true; 330 } 331 332 void CoredumpWriter::FilterSegments( 333 const std::vector<Phdr>& program_headers, 334 const FileMappings& file_mappings, 335 std::vector<Phdr>* program_headers_filtered) { 336 program_headers_filtered->resize(program_headers.size()); 337 338 // The first segment is NOTE. Use the original data unchanged. 339 (*program_headers_filtered)[0] = program_headers[0]; 340 341 for (size_t i = 1; i < program_headers.size(); ++i) { 342 Phdr& out = (*program_headers_filtered)[i]; 343 out = program_headers[i]; 344 345 // If the type is PT_LOAD and the range is found in the set, it means the 346 // segment is backed by a file. So it can be excluded as it doesn't cotnain 347 // stack data useful to generate minidump. 348 const FileRange range(out.p_vaddr, out.p_vaddr + out.p_memsz); 349 if (out.p_type == PT_LOAD && file_mappings.count(range)) { 350 out.p_filesz = 0; 351 } 352 // Calculate offset. 353 const Phdr& prev_program_header = (*program_headers_filtered)[i - 1]; 354 out.p_offset = prev_program_header.p_offset + prev_program_header.p_filesz; 355 // Offset alignment. 356 if (out.p_align != 0 && out.p_offset % out.p_align != 0) { 357 out.p_offset += out.p_align - out.p_offset % out.p_align; 358 } 359 } 360 } 361 362 bool CoredumpWriter::WriteAuxv(const std::vector<char>& note_buf, 363 const std::string& output_path) { 364 // Locate AUXV note. 365 google_breakpad::ElfCoreDump::Note note( 366 google_breakpad::MemoryRange(note_buf.data(), note_buf.size())); 367 while (note.IsValid() && note.GetType() != NT_AUXV) { 368 note = note.GetNextNote(); 369 } 370 if (!note.IsValid()) { 371 ALOGE("Failed to locate NT_AUXV."); 372 return false; 373 } 374 375 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open( 376 output_path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_EXCL, 377 S_IRUSR | S_IWUSR))); 378 if (fd == -1) { 379 ALOGE("Failed to open %s", output_path.c_str()); 380 return false; 381 } 382 // The contents of NT_AUXV is in the same format as that of /proc/[pid]/auxv. 383 return android::base::WriteFully( 384 fd, note.GetDescription().data(), note.GetDescription().length()); 385 } 386 387 bool CoredumpWriter::WriteMaps(const std::vector<Phdr>& program_headers, 388 const FileMappings& file_mappings, 389 const std::string& output_path) { 390 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open( 391 output_path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_EXCL, 392 S_IRUSR | S_IWUSR))); 393 if (fd == -1) { 394 ALOGE("Failed to open %s", output_path.c_str()); 395 return false; 396 } 397 for (const auto& program_header : program_headers) { 398 if (program_header.p_type != PT_LOAD) 399 continue; 400 const FileRange range(program_header.p_vaddr, 401 program_header.p_vaddr + program_header.p_memsz); 402 // If a mapping is found for the range, the range is mapped to a file. 403 const auto it = file_mappings.find(range); 404 const long offset = it != file_mappings.end() ? it->second.offset : 0; 405 const std::string path = it != file_mappings.end() ? it->second.path : ""; 406 407 const int kBufSize = 1024; 408 char buf[kBufSize]; 409 const int len = snprintf( 410 buf, kBufSize, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %d %s\n", 411 range.first, range.second, 412 program_header.p_flags & PF_R ? 'r' : '-', 413 program_header.p_flags & PF_W ? 'w' : '-', 414 program_header.p_flags & PF_X ? 'x' : '-', 415 'p', // Fake value: We can't know if the mapping is shared or private. 416 offset, 417 0, // Fake device (major) value. 418 0, // Fake device (minor) value. 419 0, // Fake inode value. 420 path.c_str()); 421 if (len < 0 || len > kBufSize || 422 !android::base::WriteFully(fd, buf, len)) { 423 return false; 424 } 425 } 426 return true; 427 } 428