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