1 // Copyright 2018 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "handler/linux/crash_report_exception_handler.h"
16
17 #include <memory>
18 #include <utility>
19
20 #include "base/logging.h"
21 #include "build/build_config.h"
22 #include "client/settings.h"
23 #include "handler/linux/capture_snapshot.h"
24 #include "minidump/minidump_file_writer.h"
25 #include "snapshot/linux/process_snapshot_linux.h"
26 #include "snapshot/sanitized/process_snapshot_sanitized.h"
27 #include "util/file/file_helper.h"
28 #include "util/file/file_reader.h"
29 #include "util/file/output_stream_file_writer.h"
30 #include "util/linux/direct_ptrace_connection.h"
31 #include "util/linux/ptrace_client.h"
32 #include "util/misc/implicit_cast.h"
33 #include "util/misc/metrics.h"
34 #include "util/misc/uuid.h"
35 #include "util/stream/base94_output_stream.h"
36 #include "util/stream/log_output_stream.h"
37 #include "util/stream/zlib_output_stream.h"
38
39 #if defined(OS_ANDROID)
40 #include <android/log.h>
41 #endif
42
43 namespace crashpad {
44 namespace {
45
46 class Logger final : public LogOutputStream::Delegate {
47 public:
48 Logger() = default;
49 ~Logger() override = default;
50
51 #if defined(OS_ANDROID)
Log(const char * buf)52 int Log(const char* buf) override {
53 return __android_log_buf_write(
54 LOG_ID_CRASH, ANDROID_LOG_FATAL, "crashpad", buf);
55 }
56
OutputCap()57 size_t OutputCap() override {
58 // Most minidumps are expected to be compressed and encoded into less than
59 // 128k.
60 return 128 * 1024;
61 }
62
LineWidth()63 size_t LineWidth() override {
64 // From Android NDK r20 <android/log.h>, log message text may be truncated
65 // to less than an implementation-specific limit (1023 bytes), for sake of
66 // safe and being easy to read in logcat, choose 512.
67 return 512;
68 }
69 #else
70 // TODO(jperaza): Log to an appropriate location on Linux.
Log(const char * buf)71 int Log(const char* buf) override { return -ENOTCONN; }
OutputCap()72 size_t OutputCap() override { return 0; }
LineWidth()73 size_t LineWidth() override { return 0; }
74 #endif
75
76 private:
77 DISALLOW_COPY_AND_ASSIGN(Logger);
78 };
79
WriteMinidumpLogFromFile(FileReaderInterface * file_reader)80 bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) {
81 ZlibOutputStream stream(
82 ZlibOutputStream::Mode::kCompress,
83 std::make_unique<Base94OutputStream>(
84 Base94OutputStream::Mode::kEncode,
85 std::make_unique<LogOutputStream>(std::make_unique<Logger>())));
86 FileOperationResult read_result;
87 do {
88 uint8_t buffer[4096];
89 read_result = file_reader->Read(buffer, sizeof(buffer));
90 if (read_result < 0)
91 return false;
92
93 if (read_result > 0 && (!stream.Write(buffer, read_result)))
94 return false;
95 } while (read_result > 0);
96 return stream.Flush();
97 }
98
99 } // namespace
100
CrashReportExceptionHandler(CrashReportDatabase * database,CrashReportUploadThread * upload_thread,const std::map<std::string,std::string> * process_annotations,const std::vector<base::FilePath> * attachments,bool write_minidump_to_database,bool write_minidump_to_log,const UserStreamDataSources * user_stream_data_sources)101 CrashReportExceptionHandler::CrashReportExceptionHandler(
102 CrashReportDatabase* database,
103 CrashReportUploadThread* upload_thread,
104 const std::map<std::string, std::string>* process_annotations,
105 const std::vector<base::FilePath>* attachments,
106 bool write_minidump_to_database,
107 bool write_minidump_to_log,
108 const UserStreamDataSources* user_stream_data_sources)
109 : database_(database),
110 upload_thread_(upload_thread),
111 process_annotations_(process_annotations),
112 attachments_(attachments),
113 write_minidump_to_database_(write_minidump_to_database),
114 write_minidump_to_log_(write_minidump_to_log),
115 user_stream_data_sources_(user_stream_data_sources) {
116 DCHECK(write_minidump_to_database_ | write_minidump_to_log_);
117 }
118
119 CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
120
HandleException(pid_t client_process_id,uid_t client_uid,const ExceptionHandlerProtocol::ClientInformation & info,VMAddress requesting_thread_stack_address,pid_t * requesting_thread_id,UUID * local_report_id)121 bool CrashReportExceptionHandler::HandleException(
122 pid_t client_process_id,
123 uid_t client_uid,
124 const ExceptionHandlerProtocol::ClientInformation& info,
125 VMAddress requesting_thread_stack_address,
126 pid_t* requesting_thread_id,
127 UUID* local_report_id) {
128 Metrics::ExceptionEncountered();
129
130 DirectPtraceConnection connection;
131 if (!connection.Initialize(client_process_id)) {
132 Metrics::ExceptionCaptureResult(
133 Metrics::CaptureResult::kDirectPtraceFailed);
134 return false;
135 }
136
137 return HandleExceptionWithConnection(&connection,
138 info,
139 client_uid,
140 requesting_thread_stack_address,
141 requesting_thread_id,
142 local_report_id);
143 }
144
HandleExceptionWithBroker(pid_t client_process_id,uid_t client_uid,const ExceptionHandlerProtocol::ClientInformation & info,int broker_sock,UUID * local_report_id)145 bool CrashReportExceptionHandler::HandleExceptionWithBroker(
146 pid_t client_process_id,
147 uid_t client_uid,
148 const ExceptionHandlerProtocol::ClientInformation& info,
149 int broker_sock,
150 UUID* local_report_id) {
151 Metrics::ExceptionEncountered();
152
153 PtraceClient client;
154 if (!client.Initialize(broker_sock, client_process_id)) {
155 Metrics::ExceptionCaptureResult(
156 Metrics::CaptureResult::kBrokeredPtraceFailed);
157 return false;
158 }
159
160 return HandleExceptionWithConnection(
161 &client, info, client_uid, 0, nullptr, local_report_id);
162 }
163
HandleExceptionWithConnection(PtraceConnection * connection,const ExceptionHandlerProtocol::ClientInformation & info,uid_t client_uid,VMAddress requesting_thread_stack_address,pid_t * requesting_thread_id,UUID * local_report_id)164 bool CrashReportExceptionHandler::HandleExceptionWithConnection(
165 PtraceConnection* connection,
166 const ExceptionHandlerProtocol::ClientInformation& info,
167 uid_t client_uid,
168 VMAddress requesting_thread_stack_address,
169 pid_t* requesting_thread_id,
170 UUID* local_report_id) {
171 std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
172 std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
173 if (!CaptureSnapshot(connection,
174 info,
175 *process_annotations_,
176 client_uid,
177 requesting_thread_stack_address,
178 requesting_thread_id,
179 &process_snapshot,
180 &sanitized_snapshot)) {
181 return false;
182 }
183
184 UUID client_id;
185 Settings* const settings = database_->GetSettings();
186 if (settings) {
187 // If GetSettings() or GetClientID() fails, something else will log a
188 // message and client_id will be left at its default value, all zeroes,
189 // which is appropriate.
190 settings->GetClientID(&client_id);
191 }
192 process_snapshot->SetClientID(client_id);
193
194 return write_minidump_to_database_
195 ? WriteMinidumpToDatabase(process_snapshot.get(),
196 sanitized_snapshot.get(),
197 write_minidump_to_log_,
198 local_report_id)
199 : WriteMinidumpToLog(process_snapshot.get(),
200 sanitized_snapshot.get());
201 }
202
WriteMinidumpToDatabase(ProcessSnapshotLinux * process_snapshot,ProcessSnapshotSanitized * sanitized_snapshot,bool write_minidump_to_log,UUID * local_report_id)203 bool CrashReportExceptionHandler::WriteMinidumpToDatabase(
204 ProcessSnapshotLinux* process_snapshot,
205 ProcessSnapshotSanitized* sanitized_snapshot,
206 bool write_minidump_to_log,
207 UUID* local_report_id) {
208 std::unique_ptr<CrashReportDatabase::NewReport> new_report;
209 CrashReportDatabase::OperationStatus database_status =
210 database_->PrepareNewCrashReport(&new_report);
211 if (database_status != CrashReportDatabase::kNoError) {
212 LOG(ERROR) << "PrepareNewCrashReport failed";
213 Metrics::ExceptionCaptureResult(
214 Metrics::CaptureResult::kPrepareNewCrashReportFailed);
215 return false;
216 }
217
218 process_snapshot->SetReportID(new_report->ReportID());
219
220 ProcessSnapshot* snapshot =
221 sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
222 : implicit_cast<ProcessSnapshot*>(process_snapshot);
223
224 MinidumpFileWriter minidump;
225 minidump.InitializeFromSnapshot(snapshot);
226 AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
227
228 if (!minidump.WriteEverything(new_report->Writer())) {
229 LOG(ERROR) << "WriteEverything failed";
230 Metrics::ExceptionCaptureResult(
231 Metrics::CaptureResult::kMinidumpWriteFailed);
232 return false;
233 }
234
235 bool write_minidump_to_log_succeed = false;
236 if (write_minidump_to_log) {
237 if (auto* file_reader = new_report->Reader()) {
238 if (WriteMinidumpLogFromFile(file_reader))
239 write_minidump_to_log_succeed = true;
240 else
241 LOG(ERROR) << "WriteMinidumpLogFromFile failed";
242 }
243 }
244
245 for (const auto& attachment : (*attachments_)) {
246 FileReader file_reader;
247 if (!file_reader.Open(attachment)) {
248 LOG(ERROR) << "attachment " << attachment.value().c_str()
249 << " couldn't be opened, skipping";
250 continue;
251 }
252
253 base::FilePath filename = attachment.BaseName();
254 FileWriter* file_writer = new_report->AddAttachment(filename.value());
255 if (file_writer == nullptr) {
256 LOG(ERROR) << "attachment " << filename.value().c_str()
257 << " couldn't be created, skipping";
258 continue;
259 }
260
261 CopyFileContent(&file_reader, file_writer);
262 }
263
264 UUID uuid;
265 database_status =
266 database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
267 if (database_status != CrashReportDatabase::kNoError) {
268 LOG(ERROR) << "FinishedWritingCrashReport failed";
269 Metrics::ExceptionCaptureResult(
270 Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
271 return false;
272 }
273
274 if (upload_thread_) {
275 upload_thread_->ReportPending(uuid);
276 }
277
278 if (local_report_id != nullptr) {
279 *local_report_id = uuid;
280 }
281
282 Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
283
284 return write_minidump_to_log ? write_minidump_to_log_succeed : true;
285 }
286
WriteMinidumpToLog(ProcessSnapshotLinux * process_snapshot,ProcessSnapshotSanitized * sanitized_snapshot)287 bool CrashReportExceptionHandler::WriteMinidumpToLog(
288 ProcessSnapshotLinux* process_snapshot,
289 ProcessSnapshotSanitized* sanitized_snapshot) {
290 ProcessSnapshot* snapshot =
291 sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
292 : implicit_cast<ProcessSnapshot*>(process_snapshot);
293 MinidumpFileWriter minidump;
294 minidump.InitializeFromSnapshot(snapshot);
295 AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
296
297 OutputStreamFileWriter writer(std::make_unique<ZlibOutputStream>(
298 ZlibOutputStream::Mode::kCompress,
299 std::make_unique<Base94OutputStream>(
300 Base94OutputStream::Mode::kEncode,
301 std::make_unique<LogOutputStream>(std::make_unique<Logger>()))));
302 if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) {
303 LOG(ERROR) << "WriteMinidump failed";
304 return false;
305 }
306 return writer.Flush();
307 }
308
309 } // namespace crashpad
310