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 
GetFreeDiskSpace(const std::string & path)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 
Seek(int fd,off_t offset)46 bool Seek(int fd, off_t offset) {
47   return lseek(fd, offset, SEEK_SET) == offset;
48 }
49 
50 template<typename T>
GetValueFromNote(const google_breakpad::ElfCoreDump::Note & note,size_t offset,T default_value)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:
FdReader(int fd)62   explicit FdReader(int fd) : fd_(fd), bytes_read_(0) {}
63 
64   // Reads the given number of bytes.
Read(void * buf,size_t num_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.
CopyTo(int fd_dest,size_t num_bytes)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.
Seek(size_t offset)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 
CoredumpWriter(int fd_src,const std::string & coredump_filename,const std::string & proc_files_dir)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 
~CoredumpWriter()113 CoredumpWriter::~CoredumpWriter() {
114 }
115 
WriteCoredump()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 
WriteCoredumpToFD(int fd_dest)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 
ReadUntilNote(FdReader * reader,Ehdr * elf_header,std::vector<Phdr> * program_headers,std::vector<char> * note_buf)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 
GetFileMappings(const std::vector<char> & note_buf,FileMappings * file_mappings)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 
FilterSegments(const std::vector<Phdr> & program_headers,const FileMappings & file_mappings,std::vector<Phdr> * program_headers_filtered)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 
WriteAuxv(const std::vector<char> & note_buf,const std::string & output_path)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 
WriteMaps(const std::vector<Phdr> & program_headers,const FileMappings & file_mappings,const std::string & output_path)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