Home | History | Annotate | Download | only in crash_collector
      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, &note_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