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