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 <log/logger.h> 28 #include <nativehelper/ScopedFd.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 ScopedFd fd_dest(TEMP_FAILURE_RETRY(open(coredump_filename_.c_str(), 118 O_WRONLY | O_CREAT | O_EXCL, 119 S_IRUSR | S_IWUSR))); 120 if (fd_dest.get() == -1) { 121 ALOGE("Failed to open: %s, errno = %d", coredump_filename_.c_str(), errno); 122 return -1; 123 } 124 ssize_t result = WriteCoredumpToFD(fd_dest.get()); 125 fd_dest.reset(); 126 if (result == -1) 127 unlink(coredump_filename_.c_str()); 128 return result; 129 } 130 131 ssize_t CoredumpWriter::WriteCoredumpToFD(int fd_dest) { 132 // Input coredump is generated by kernel's fs/binfmt_elf.c and formatted like: 133 // 134 // ELF Header 135 // Program Header 1 136 // Program Header 2 137 // ... 138 // Program Header n 139 // Segment 1 (This segment's type should be PT_NOTE) 140 // Segment 2 141 // ... 142 // Segment n 143 144 // First, read ELF Header, all program headers, and the first segment whose 145 // type is PT_NOTE. 146 FdReader reader(fd_src_); 147 Ehdr elf_header; 148 std::vector<Phdr> program_headers; 149 std::vector<char> note_buf; 150 if (!ReadUntilNote(&reader, &elf_header, &program_headers, ¬e_buf)) { 151 return -1; 152 } 153 // Get a set of address ranges occupied by mapped files from NOTE. 154 FileMappings file_mappings; 155 if (!GetFileMappings(note_buf, &file_mappings)) { 156 return -1; 157 } 158 // Filter out segments backed by mapped files as they are useless when 159 // generating minidump. 160 std::vector<Phdr> program_headers_filtered; 161 FilterSegments(program_headers, file_mappings, &program_headers_filtered); 162 163 // Calculate the coredump size limit. 164 const int64_t free_disk_space = GetFreeDiskSpace(coredump_filename_); 165 if (free_disk_space < 0) { 166 return -1; 167 } 168 coredump_size_limit_ = std::min(static_cast<size_t>(free_disk_space / 20), 169 kMaxCoredumpSize); 170 171 // Calculate the output file size. 172 expected_coredump_size_ = program_headers_filtered.back().p_offset + 173 program_headers_filtered.back().p_filesz; 174 if (expected_coredump_size_ > coredump_size_limit_) { 175 ALOGE("Coredump too large: %zu", expected_coredump_size_); 176 return -1; 177 } 178 179 // Write proc files. 180 if (!WriteAuxv(note_buf, proc_files_dir_ + "/auxv") || 181 !WriteMaps(program_headers, file_mappings, proc_files_dir_ + "/maps")) { 182 return -1; 183 } 184 185 // Write ELF header. 186 if (!android::base::WriteFully(fd_dest, &elf_header, sizeof(elf_header))) { 187 ALOGE("Failed to write ELF header."); 188 return -1; 189 } 190 // Write program headers. 191 for (size_t i = 0; i < program_headers_filtered.size(); ++i) { 192 const Phdr& program_header = program_headers_filtered[i]; 193 const size_t offset = sizeof(elf_header) + i * elf_header.e_phentsize; 194 if (!Seek(fd_dest, offset) || 195 !android::base::WriteFully(fd_dest, &program_header, 196 sizeof(program_header))) { 197 ALOGE("Failed to write program header: i = %zu", i); 198 return -1; 199 } 200 } 201 // Write NOTE segment. 202 if (!Seek(fd_dest, program_headers_filtered[0].p_offset) || 203 !android::base::WriteFully(fd_dest, note_buf.data(), note_buf.size())) { 204 ALOGE("Failed to write NOTE."); 205 return -1; 206 } 207 // Read all remaining segments and write some of them. 208 for (size_t i = 1; i < program_headers_filtered.size(); ++i) { 209 const Phdr& program_header = program_headers_filtered[i]; 210 if (program_header.p_filesz > 0) { 211 const Phdr& program_header_original = program_headers[i]; 212 if (!reader.Seek(program_header_original.p_offset)) { 213 ALOGE("Failed to seek segment: i = %zu", i); 214 return -1; 215 } 216 if (!Seek(fd_dest, program_header.p_offset) || 217 !reader.CopyTo(fd_dest, program_header.p_filesz)) { 218 ALOGE("Failed to write segment: i = %zu", i); 219 return -1; 220 } 221 } 222 } 223 return expected_coredump_size_; 224 } 225 226 bool CoredumpWriter::ReadUntilNote(FdReader* reader, 227 Ehdr* elf_header, 228 std::vector<Phdr>* program_headers, 229 std::vector<char>* note_buf) { 230 // Read ELF header. 231 if (!reader->Read(elf_header, sizeof(*elf_header)) || 232 memcmp(elf_header->e_ident, ELFMAG, SELFMAG) != 0 || 233 elf_header->e_ident[EI_CLASS] != google_breakpad::ElfCoreDump::kClass || 234 elf_header->e_version != EV_CURRENT || 235 elf_header->e_type != ET_CORE || 236 elf_header->e_ehsize != sizeof(Ehdr) || 237 elf_header->e_phentsize != sizeof(Phdr)) { 238 ALOGE("Failed to read ELF header."); 239 return false; 240 } 241 242 // Read program headers; 243 program_headers->resize(elf_header->e_phnum); 244 if (!reader->Seek(elf_header->e_phoff) || 245 !reader->Read(program_headers->data(), 246 sizeof(Phdr) * program_headers->size())) { 247 ALOGE("Failed to read program headers."); 248 return false; 249 } 250 251 // The first segment should be NOTE. 252 if (program_headers->size() < 1 || 253 (*program_headers)[0].p_type != PT_NOTE) { 254 ALOGE("Failed to locate NOTE."); 255 return false; 256 } 257 const Phdr& note_program_header = (*program_headers)[0]; 258 259 // Read NOTE segment. 260 note_buf->resize(note_program_header.p_filesz); 261 if (!reader->Seek(note_program_header.p_offset) || 262 !reader->Read(note_buf->data(), note_buf->size())) { 263 ALOGE("Failed to read NOTE."); 264 return false; 265 } 266 return true; 267 } 268 269 bool CoredumpWriter::GetFileMappings(const std::vector<char>& note_buf, 270 FileMappings* file_mappings) { 271 // Locate FILE note. 272 google_breakpad::ElfCoreDump::Note note( 273 google_breakpad::MemoryRange(note_buf.data(), note_buf.size())); 274 while (note.IsValid() && note.GetType() != NT_FILE) { 275 note = note.GetNextNote(); 276 } 277 if (!note.IsValid()) { 278 ALOGE("Failed to locate NT_FILE."); 279 return false; 280 } 281 282 // NT_FILE note format: (see kernel's fs/binfmt_elf.c for details) 283 // Number of mapped files 284 // Page size 285 // Start address of file 1 286 // End address of file 1 287 // Offset of file 1 288 // Start address of file 2 289 // ... 290 // Offset of file n 291 // File name 1 (null-terminated) 292 // File name 2 293 // ... 294 // File name n 295 const long kInvalidValue = -1; 296 const long file_count = GetValueFromNote<long>(note, 0, kInvalidValue); 297 const long page_size = GetValueFromNote<long>(note, sizeof(long), 298 kInvalidValue); 299 if (file_count == kInvalidValue || page_size == kInvalidValue) { 300 ALOGE("Invalid FILE note."); 301 return false; 302 } 303 // Read contents of FILE note. 304 size_t filename_pos = sizeof(long) * (2 + 3 * file_count); 305 for (long i = 0; i < file_count; ++i) { 306 const long start = GetValueFromNote<long>( 307 note, sizeof(long) * (2 + 3 * i), kInvalidValue); 308 const long end = GetValueFromNote<long>( 309 note, sizeof(long) * (2 + 3 * i + 1), kInvalidValue); 310 const long offset = GetValueFromNote<long>( 311 note, sizeof(long) * (2 + 3 * i + 2), kInvalidValue); 312 if (start == kInvalidValue || end == kInvalidValue || 313 offset == kInvalidValue) { 314 ALOGE("Invalid FILE Note."); 315 return false; 316 } 317 // Add a new mapping. 318 FileInfo& info = (*file_mappings)[std::make_pair(start, end)]; 319 info.offset = offset * page_size; 320 // Read file name. 321 while (true) { 322 const char c = GetValueFromNote<char>(note, filename_pos++, 0); 323 if (!c) 324 break; 325 info.path.push_back(c); 326 } 327 } 328 return true; 329 } 330 331 void CoredumpWriter::FilterSegments( 332 const std::vector<Phdr>& program_headers, 333 const FileMappings& file_mappings, 334 std::vector<Phdr>* program_headers_filtered) { 335 program_headers_filtered->resize(program_headers.size()); 336 337 // The first segment is NOTE. Use the original data unchanged. 338 (*program_headers_filtered)[0] = program_headers[0]; 339 340 for (size_t i = 1; i < program_headers.size(); ++i) { 341 Phdr& out = (*program_headers_filtered)[i]; 342 out = program_headers[i]; 343 344 // If the type is PT_LOAD and the range is found in the set, it means the 345 // segment is backed by a file. So it can be excluded as it doesn't cotnain 346 // stack data useful to generate minidump. 347 const FileRange range(out.p_vaddr, out.p_vaddr + out.p_memsz); 348 if (out.p_type == PT_LOAD && file_mappings.count(range)) { 349 out.p_filesz = 0; 350 } 351 // Calculate offset. 352 const Phdr& prev_program_header = (*program_headers_filtered)[i - 1]; 353 out.p_offset = prev_program_header.p_offset + prev_program_header.p_filesz; 354 // Offset alignment. 355 if (out.p_align != 0 && out.p_offset % out.p_align != 0) { 356 out.p_offset += out.p_align - out.p_offset % out.p_align; 357 } 358 } 359 } 360 361 bool CoredumpWriter::WriteAuxv(const std::vector<char>& note_buf, 362 const std::string& output_path) { 363 // Locate AUXV note. 364 google_breakpad::ElfCoreDump::Note note( 365 google_breakpad::MemoryRange(note_buf.data(), note_buf.size())); 366 while (note.IsValid() && note.GetType() != NT_AUXV) { 367 note = note.GetNextNote(); 368 } 369 if (!note.IsValid()) { 370 ALOGE("Failed to locate NT_AUXV."); 371 return false; 372 } 373 374 ScopedFd fd(TEMP_FAILURE_RETRY(open( 375 output_path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_EXCL, 376 S_IRUSR | S_IWUSR))); 377 if (fd.get() == -1) { 378 ALOGE("Failed to open %s", output_path.c_str()); 379 return false; 380 } 381 // The contents of NT_AUXV is in the same format as that of /proc/[pid]/auxv. 382 return android::base::WriteFully( 383 fd.get(), note.GetDescription().data(), note.GetDescription().length()); 384 } 385 386 bool CoredumpWriter::WriteMaps(const std::vector<Phdr>& program_headers, 387 const FileMappings& file_mappings, 388 const std::string& output_path) { 389 ScopedFd fd(TEMP_FAILURE_RETRY(open( 390 output_path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_EXCL, 391 S_IRUSR | S_IWUSR))); 392 if (fd.get() == -1) { 393 ALOGE("Failed to open %s", output_path.c_str()); 394 return false; 395 } 396 for (const auto& program_header : program_headers) { 397 if (program_header.p_type != PT_LOAD) 398 continue; 399 const FileRange range(program_header.p_vaddr, 400 program_header.p_vaddr + program_header.p_memsz); 401 // If a mapping is found for the range, the range is mapped to a file. 402 const auto it = file_mappings.find(range); 403 const long offset = it != file_mappings.end() ? it->second.offset : 0; 404 const std::string path = it != file_mappings.end() ? it->second.path : ""; 405 406 const int kBufSize = 1024; 407 char buf[kBufSize]; 408 const int len = snprintf( 409 buf, kBufSize, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %d %s\n", 410 range.first, range.second, 411 program_header.p_flags & PF_R ? 'r' : '-', 412 program_header.p_flags & PF_W ? 'w' : '-', 413 program_header.p_flags & PF_X ? 'x' : '-', 414 'p', // Fake value: We can't know if the mapping is shared or private. 415 offset, 416 0, // Fake device (major) value. 417 0, // Fake device (minor) value. 418 0, // Fake inode value. 419 path.c_str()); 420 if (len < 0 || len > kBufSize || 421 !android::base::WriteFully(fd.get(), buf, len)) { 422 return false; 423 } 424 } 425 return true; 426 } 427