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 <dirent.h>
20 #include <errno.h>
21 #include <pwd.h>
22 #include <sys/capability.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 
26 #include <string>
27 #include <vector>
28 
29 #include <android-base/file.h>
30 #include <cutils/properties.h>
31 #include <log/log.h>
32 #include <private/android_filesystem_config.h>
33 #include <utils/String8.h>
34 
35 #include "client/linux/minidump_writer/linux_core_dumper.h"
36 #include "client/linux/minidump_writer/minidump_writer.h"
37 
38 #undef DISALLOW_COPY_AND_ASSIGN  // Defined in breakpad's header.
39 #include "coredump_writer.h"
40 
41 namespace {
42 
43 using android::String8;
44 
45 const char kOutputDirectory[] = "/data/system/crash_reports";
46 const int kMaxNumReports = 16;
47 
48 // Gets a list of entries under the specified directory.
ReadDirectory(const std::string & path,std::vector<dirent> * entries)49 bool ReadDirectory(const std::string& path, std::vector<dirent>* entries) {
50   std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
51   if (!dir)
52     return false;
53   while (struct dirent* entry = readdir(dir.get())) {
54     if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
55       entries->push_back(*entry);
56   }
57   return true;
58 }
59 
60 // Removes a file or a directory recursively.
RemoveRecursively(const std::string & path)61 bool RemoveRecursively(const std::string& path) {
62   if (unlink(path.c_str()) == 0)
63     return true;
64   if (errno != EISDIR) {
65     ALOGE("Failed to unlink: %s, errno = %d", path.c_str(), errno);
66     return false;
67   }
68   std::vector<dirent> entries;
69   if (!ReadDirectory(path, &entries))
70     return false;
71   for (const auto& entry : entries) {
72     if (!RemoveRecursively(path + "/" + entry.d_name))
73       return false;
74   }
75   return rmdir(path.c_str()) == 0;
76 }
77 
78 // Makes room for the new crash report by deleting old files when necessary.
MakeRoomForNewReport()79 bool MakeRoomForNewReport() {
80   // Enumerate reports.
81   std::vector<dirent> entries;
82   if (!ReadDirectory(kOutputDirectory, &entries))
83     return false;
84 
85   std::vector<time_t> dump_mtimes;  // Modification time of dump files.
86   std::vector<std::pair<time_t, String8>> all_files;
87   for (const auto& entry : entries) {
88     String8 filename = String8(kOutputDirectory).appendPath(entry.d_name);
89     struct stat attributes;
90     if (stat(filename.string(), &attributes))
91       return false;
92     all_files.push_back(std::make_pair(attributes.st_mtime, filename));
93     if (filename.getPathExtension() == ".dmp")
94       dump_mtimes.push_back(attributes.st_mtime);
95   }
96 
97   // Remove old files.
98   if (dump_mtimes.size() >= kMaxNumReports) {
99     // Sort the vector (newer file comes first).
100     std::sort(dump_mtimes.rbegin(), dump_mtimes.rend());
101 
102     const time_t threshold = dump_mtimes[kMaxNumReports - 1];
103     for (const auto& file : all_files) {
104       const time_t mtime = file.first;
105       const String8& filename = file.second;
106       if (mtime <= threshold) {
107         if (!RemoveRecursively(filename.string()))
108           return false;
109       }
110     }
111   }
112   return true;
113 }
114 
115 // Returns the specified system property.
GetSystemProperty(const std::string & key)116 std::string GetSystemProperty(const std::string& key) {
117   char buf[PROPERTY_VALUE_MAX];
118   property_get(key.c_str(), buf, "");
119   return std::string(buf);
120 }
121 
122 // Writes metadata as JSON file.
WriteMetadata(ssize_t result_coredump_size,size_t coredump_size_limit,size_t expected_coredump_size,const std::string & pid,const std::string & uid,const std::string & gid,const std::string & signal,const std::string & username,const std::string & exec_name,const std::string & filename)123 bool WriteMetadata(ssize_t result_coredump_size,
124                    size_t coredump_size_limit,
125                    size_t expected_coredump_size,
126                    const std::string& pid,
127                    const std::string& uid,
128                    const std::string& gid,
129                    const std::string& signal,
130                    const std::string& username,
131                    const std::string& exec_name,
132                    const std::string& filename) {
133   std::string content = "{";
134   content += "\"version\":\"" + GetSystemProperty("ro.build.id") + "\"";
135   content += ",";
136   content += "\"result_coredump_size\":" + std::to_string(result_coredump_size);
137   content += ",";
138   content += "\"coredump_size_limit\":" + std::to_string(coredump_size_limit);
139   content += ",";
140   content += "\"expected_coredump_size\":" +
141       std::to_string(expected_coredump_size);
142   content += ",";
143   content += "\"pid\":" + pid;
144   content += ",";
145   content += "\"uid\":" + uid;
146   content += ",";
147   content += "\"gid\":" + gid;
148   content += ",";
149   content += "\"signal\":" + signal;
150   content += ",";
151   content += "\"username\":\"" + username + "\"";
152   content += ",";
153   content += "\"process\":\"" + exec_name + "\"";
154   content += "}";
155   return android::base::WriteStringToFile(
156       content, filename, S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
157 }
158 
159 // Converts the specified coredump file to a minidump.
ConvertCoredumpToMinidump(const std::string & coredump_filename,const std::string & proc_files_dir,const std::string & minidump_filename)160 bool ConvertCoredumpToMinidump(const std::string& coredump_filename,
161                                const std::string& proc_files_dir,
162                                const std::string& minidump_filename) {
163   google_breakpad::MappingList mappings;
164   google_breakpad::AppMemoryList memory_list;
165   google_breakpad::LinuxCoreDumper dumper(
166       0, coredump_filename.c_str(), proc_files_dir.c_str());
167   bool success = google_breakpad::WriteMinidump(
168       minidump_filename.c_str(), mappings, memory_list, &dumper);
169   unlink(coredump_filename.c_str());
170   RemoveRecursively(proc_files_dir);
171   return success;
172 }
173 
174 }  // namespace
175 
main(int argc,char ** argv)176 int main(int argc, char** argv) {
177   if (argc < 7) {
178     ALOGE("Insufficient args.");
179     return 1;
180   }
181   const std::string pid_string = argv[1];
182   const std::string uid_string = argv[2];
183   const std::string gid_string = argv[3];
184   const std::string signal_string = argv[4];
185   const std::string crash_time = argv[5];
186   const std::string exec_name = argv[6];
187 
188   const uid_t uid = std::stoi(uid_string);
189   const uid_t appid = uid % AID_USER;
190   if (appid >= AID_APP) {  // Ignore non-system crashes.
191     return 0;
192   }
193 
194   // Act as the system user and drop all capabilities.
195   struct __user_cap_header_struct capheader;
196   struct __user_cap_data_struct capdata[2];
197   memset(&capheader, 0, sizeof(capheader));
198   memset(&capdata, 0, sizeof(capdata));
199   capheader.version = _LINUX_CAPABILITY_VERSION_3;
200   if (setegid(AID_SYSTEM) != 0 || seteuid(AID_SYSTEM) != 0 ||
201       capset(&capheader, capdata) != 0) {
202     ALOGE("Failed to stop being root.");
203     return 1;
204   }
205 
206   // Username lookup.
207   passwd* pwd = getpwuid(appid);
208   std::string username((pwd != NULL) ? pwd->pw_name : "");
209   // Delete old crash reports.
210   if (!MakeRoomForNewReport()) {
211     ALOGE("Failed to delete old crash reports.");
212     return 1;
213   }
214   // Read coredump from stdin.
215   const std::string basename =
216       std::string(kOutputDirectory) + "/" + crash_time + "." + pid_string;
217   const std::string coredump = basename + ".core";
218   const std::string proc_files_dir = basename + ".proc";
219   if (mkdir(proc_files_dir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
220     ALOGE("Failed to create proc directory. errno = %d", errno);
221     return 1;
222   }
223   CoredumpWriter coredump_writer(STDIN_FILENO, coredump, proc_files_dir);
224   const ssize_t result_coredump_size = coredump_writer.WriteCoredump();
225   if (result_coredump_size > 0) {
226     // Convert coredump to minidump.
227     const std::string minidump = basename + ".dmp";
228     if (!ConvertCoredumpToMinidump(coredump, proc_files_dir, minidump)) {
229       ALOGE("Failed to convert coredump to minidump.");
230     }
231   } else {
232     ALOGE("Failed to copy coredump from stdin.");
233   }
234   // Write metadata.
235   const std::string metadata = basename + ".meta";
236   if (!WriteMetadata(result_coredump_size, coredump_writer.coredump_size_limit(),
237                      coredump_writer.expected_coredump_size(), pid_string,
238                      uid_string, gid_string, signal_string, username, exec_name,
239                      metadata)) {
240     ALOGE("Failed to write metadata.");
241     return 1;
242   }
243   return 0;
244 }
245