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 <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, &note_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