Commit 59582aea authored by Joshua Peraza's avatar Joshua Peraza Committed by Commit Bot

Add tools for dumping WebView embedder apps

Adds DumpWithoutCrashingForClient() which enables generating a dump on
behalf of the embedding app (represented by AwDebugCrashReporterClient)

This also includes refactoring of native code in minidump_uploader
to expose MimeifyReport() and WriteBodyToFile(), to be used by
AwDebug::DumpWithoutCrashing() in the followup CL.

Change-Id: If528bf060b6acaa51d0facaf1f1adf784c1852e6
Reviewed-on: https://chromium-review.googlesource.com/c/1269099
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612955}
parent 59f9c856
......@@ -11,6 +11,7 @@ include_rules = [
"+components/autofill/core/browser",
"+components/autofill/core/common",
"+components/cdm/browser",
"+components/crash/content/app",
"+components/crash/content/browser",
"+components/crash/core",
"+components/download/public/common",
......
......@@ -2,15 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/common/aw_channel.h"
#include "android_webview/common/crash_reporter/aw_crash_reporter_client.h"
#include "android_webview/common/crash_reporter/crash_keys.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/debug/dump_without_crashing.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/threading/thread_restrictions.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "components/crash/core/common/crash_key.h"
#include "components/version_info/version_info.h"
#include "components/version_info/version_info_values.h"
#include "jni/AwDebug_jni.h"
using base::android::ConvertJavaStringToUTF16;
......@@ -20,6 +25,45 @@ using base::android::ScopedJavaLocalRef;
namespace android_webview {
namespace {
class AwDebugCrashReporterClient
: public ::crash_reporter::CrashReporterClient {
public:
AwDebugCrashReporterClient() = default;
~AwDebugCrashReporterClient() override = default;
void GetProductNameAndVersion(std::string* product_name,
std::string* version,
std::string* channel) override {
*product_name = "AndroidWebView";
*version = PRODUCT_VERSION;
*channel =
version_info::GetChannelString(android_webview::GetChannelOrStable());
}
bool GetCrashDumpLocation(base::FilePath* debug_dir) override {
base::FilePath cache_dir;
if (!base::android::GetCacheDirectory(&cache_dir)) {
return false;
}
*debug_dir = cache_dir.Append(FILE_PATH_LITERAL("WebView")).Append("Debug");
return true;
}
void GetSanitizationInformation(const char* const** annotations_whitelist,
void** target_module,
bool* sanitize_stacks) override {
*annotations_whitelist = crash_keys::kWebViewCrashKeyWhiteList;
*target_module = nullptr;
*sanitize_stacks = true;
}
DISALLOW_COPY_AND_ASSIGN(AwDebugCrashReporterClient);
};
} // namespace
static jboolean JNI_AwDebug_DumpWithoutCrashing(
JNIEnv* env,
const JavaParamRef<jstring>& dump_path) {
......
......@@ -148,6 +148,14 @@ base::FilePath::StringType::const_pointer GetCrashpadDatabasePathImpl();
void DumpProcessWithoutCrashing(task_t task_port);
#endif
#if defined(OS_ANDROID)
// This is used by WebView to generate a dump on behalf of the embedding app.
// This function can only be called from the browser process. Returns `true` on
// success.
class CrashReporterClient;
bool DumpWithoutCrashingForClient(CrashReporterClient* client);
#endif // OS_ANDROID
namespace internal {
#if defined(OS_WIN)
......
......@@ -43,18 +43,40 @@
namespace crashpad {
namespace {
bool SetSanitizationInfo(SanitizationInformation* info) {
bool SetSanitizationInfo(crash_reporter::CrashReporterClient* client,
SanitizationInformation* info) {
const char* const* whitelist = nullptr;
void* target_module = nullptr;
bool sanitize_stacks = false;
crash_reporter::GetCrashReporterClient()->GetSanitizationInformation(
&whitelist, &target_module, &sanitize_stacks);
client->GetSanitizationInformation(&whitelist, &target_module,
&sanitize_stacks);
info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist);
info->target_module_address = FromPointerCast<VMAddress>(target_module);
info->sanitize_stacks = sanitize_stacks;
return whitelist != nullptr || target_module != nullptr || sanitize_stacks;
}
void SetExceptionInformation(siginfo_t* siginfo,
ucontext_t* context,
ExceptionInformation* info) {
info->siginfo_address =
FromPointerCast<decltype(info->siginfo_address)>(siginfo);
info->context_address =
FromPointerCast<decltype(info->context_address)>(context);
info->thread_id = sandbox::sys_gettid();
}
void SetClientInformation(ExceptionInformation* exception,
SanitizationInformation* sanitization,
ClientInformation* info) {
info->exception_information_address =
FromPointerCast<decltype(info->exception_information_address)>(exception);
info->sanitization_information_address =
FromPointerCast<decltype(info->sanitization_information_address)>(
sanitization);
}
// A signal handler for non-browser processes in the sandbox.
// Sends a message to a crashpad::CrashHandlerHost to handle the crash.
class SandboxedHandler {
......@@ -65,7 +87,8 @@ class SandboxedHandler {
}
bool Initialize() {
SetSanitizationInfo(&sanitization_);
SetSanitizationInfo(crash_reporter::GetCrashReporterClient(),
&sanitization_);
server_fd_ = base::GlobalDescriptors::GetInstance()->Get(
service_manager::kCrashDumpSignal);
......@@ -124,22 +147,12 @@ class SandboxedHandler {
base::ScopedFD connection;
if (state->ConnectToHandler(signo, &connection) == 0) {
ExceptionInformation exception_information;
exception_information.siginfo_address =
FromPointerCast<decltype(exception_information.siginfo_address)>(
siginfo);
exception_information.context_address =
FromPointerCast<decltype(exception_information.context_address)>(
context);
exception_information.thread_id = sandbox::sys_gettid();
ClientInformation info = {};
info.exception_information_address =
FromPointerCast<decltype(info.exception_information_address)>(
&exception_information);
info.sanitization_information_address =
FromPointerCast<decltype(info.sanitization_information_address)>(
&state->sanitization_);
SetExceptionInformation(siginfo, static_cast<ucontext_t*>(context),
&exception_information);
ClientInformation info;
SetClientInformation(&exception_information, &state->sanitization_,
&info);
ExceptionHandlerClient handler_client(connection.get());
handler_client.SetCanSetPtracer(false);
......@@ -251,12 +264,12 @@ const char kCrashpadJavaMain[] =
#endif // OS_ANDROID
void BuildHandlerArgs(base::FilePath* database_path,
void BuildHandlerArgs(CrashReporterClient* crash_reporter_client,
base::FilePath* database_path,
base::FilePath* metrics_path,
std::string* url,
std::map<std::string, std::string>* process_annotations,
std::vector<std::string>* arguments) {
CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
crash_reporter_client->GetCrashDumpLocation(database_path);
crash_reporter_client->GetCrashMetricsLocation(metrics_path);
......@@ -358,8 +371,8 @@ class HandlerStarter {
std::string url;
std::map<std::string, std::string> process_annotations;
std::vector<std::string> arguments;
BuildHandlerArgs(&database_path, &metrics_path, &url, &process_annotations,
&arguments);
BuildHandlerArgs(GetCrashReporterClient(), &database_path, &metrics_path,
&url, &process_annotations, &arguments);
base::FilePath exe_dir;
base::FilePath handler_path;
......@@ -367,7 +380,8 @@ class HandlerStarter {
return database_path;
}
if (crashpad::SetSanitizationInfo(&browser_sanitization_info_)) {
if (crashpad::SetSanitizationInfo(GetCrashReporterClient(),
&browser_sanitization_info_)) {
arguments.push_back(base::StringPrintf("--sanitization-information=%p",
&browser_sanitization_info_));
}
......@@ -399,14 +413,14 @@ class HandlerStarter {
return database_path;
}
bool StartHandlerForClient(int fd) {
bool StartHandlerForClient(CrashReporterClient* client, int fd) {
base::FilePath database_path;
base::FilePath metrics_path;
std::string url;
std::map<std::string, std::string> process_annotations;
std::vector<std::string> arguments;
BuildHandlerArgs(&database_path, &metrics_path, &url, &process_annotations,
&arguments);
BuildHandlerArgs(client, &database_path, &metrics_path, &url,
&process_annotations, &arguments);
base::FilePath exe_dir;
base::FilePath handler_path;
......@@ -449,12 +463,58 @@ class HandlerStarter {
DISALLOW_COPY_AND_ASSIGN(HandlerStarter);
};
bool ConnectToHandler(CrashReporterClient* client, base::ScopedFD* connection) {
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
PLOG(ERROR) << "socketpair";
return false;
}
base::ScopedFD local_connection(fds[0]);
base::ScopedFD handlers_socket(fds[1]);
if (!HandlerStarter::Get()->StartHandlerForClient(client,
handlers_socket.get())) {
return false;
}
*connection = std::move(local_connection);
return true;
}
} // namespace
bool DumpWithoutCrashingForClient(CrashReporterClient* client) {
base::ScopedFD connection;
if (!ConnectToHandler(client, &connection)) {
return false;
}
siginfo_t siginfo;
siginfo.si_signo = crashpad::Signals::kSimulatedSigno;
siginfo.si_errno = 0;
siginfo.si_code = 0;
ucontext_t context;
crashpad::CaptureContext(&context);
crashpad::SanitizationInformation sanitization;
crashpad::SetSanitizationInfo(client, &sanitization);
crashpad::ExceptionInformation exception;
crashpad::SetExceptionInformation(&siginfo, &context, &exception);
crashpad::ClientInformation info;
crashpad::SetClientInformation(&exception, &sanitization, &info);
crashpad::ExceptionHandlerClient handler_client(connection.get());
return handler_client.RequestCrashDump(info) == 0;
}
namespace internal {
bool StartHandlerForClient(int fd) {
return HandlerStarter::Get()->StartHandlerForClient(fd);
return HandlerStarter::Get()->StartHandlerForClient(GetCrashReporterClient(),
fd);
}
base::FilePath PlatformCrashpadInitialization(
......
......@@ -18,6 +18,13 @@
namespace minidump_uploader {
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport& report,
crashpad::HTTPMultipartBuilder* http_multipart_builder,
pid_t* pid);
bool WriteBodyToFile(crashpad::HTTPBodyStream* body,
crashpad::FileWriterInterface* writer);
namespace {
#if defined(OS_ANDROID)
......@@ -32,9 +39,32 @@ enum class ProcessedMinidumpCounts {
};
#endif // OS_ANDROID
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
const base::FilePath& dest_dir) {
crashpad::FileReader* reader = report->Reader();
bool MimeifyReportAndWriteToDirectory(
const crashpad::CrashReportDatabase::UploadReport& report,
const base::FilePath& dest_dir) {
crashpad::HTTPMultipartBuilder builder;
pid_t pid;
if (!MimeifyReport(report, &builder, &pid)) {
return false;
}
crashpad::FileWriter writer;
if (!writer.Open(dest_dir.Append(base::StringPrintf(
"%s.dmp%d", report.uuid.ToString().c_str(), pid)),
crashpad::FileWriteMode::kCreateOrFail,
crashpad::FilePermissions::kOwnerOnly)) {
return false;
}
return WriteBodyToFile(builder.GetBodyStream().get(), &writer);
}
} // namespace
bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport& report,
crashpad::HTTPMultipartBuilder* http_multipart_builder,
pid_t* pid) {
crashpad::FileReader* reader = report.Reader();
crashpad::FileOffset start_offset = reader->SeekGet();
if (start_offset < 0) {
return false;
......@@ -55,8 +85,6 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
return false;
}
crashpad::HTTPMultipartBuilder http_multipart_builder;
static constexpr char kMinidumpKey[] = "upload_file_minidump";
static constexpr char kPtypeKey[] = "ptype";
......@@ -65,7 +93,7 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
LOG(WARNING) << "reserved key " << kv.first << ", discarding value "
<< kv.second;
} else {
http_multipart_builder.SetFormData(kv.first, kv.second);
http_multipart_builder->SetFormData(kv.first, kv.second);
#if defined(OS_ANDROID)
if (kv.first == kPtypeKey) {
ProcessedMinidumpCounts count_type;
......@@ -85,31 +113,24 @@ bool MimeifyReport(const crashpad::CrashReportDatabase::UploadReport* report,
}
}
http_multipart_builder.SetFileAttachment(kMinidumpKey,
report->uuid.ToString() + ".dmp",
reader, "application/octet-stream");
http_multipart_builder->SetFileAttachment(kMinidumpKey,
report.uuid.ToString() + ".dmp",
reader, "application/octet-stream");
std::unique_ptr<crashpad::HTTPBodyStream> body =
http_multipart_builder.GetBodyStream();
crashpad::FileWriter writer;
if (!writer.Open(dest_dir.Append(base::StringPrintf(
"%s.dmp%d", report->uuid.ToString().c_str(),
minidump_process_snapshot.ProcessID())),
crashpad::FileWriteMode::kCreateOrFail,
crashpad::FilePermissions::kOwnerOnly)) {
return false;
}
*pid = minidump_process_snapshot.ProcessID();
return true;
}
bool WriteBodyToFile(crashpad::HTTPBodyStream* body,
crashpad::FileWriterInterface* writer) {
uint8_t buffer[4096];
crashpad::FileOperationResult bytes_read;
while ((bytes_read = body->GetBytesBuffer(buffer, sizeof(buffer))) > 0) {
writer.Write(buffer, bytes_read);
writer->Write(buffer, bytes_read);
}
return bytes_read == 0;
}
} // namespace
void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir,
const base::FilePath& dest_dir) {
std::unique_ptr<crashpad::CrashReportDatabase> db =
......@@ -135,7 +156,7 @@ void RewriteMinidumpsAsMIMEs(const base::FilePath& src_dir,
continue;
case crashpad::CrashReportDatabase::kNoError:
if (MimeifyReport(upload_report.get(), dest_dir)) {
if (MimeifyReportAndWriteToDirectory(*upload_report.get(), dest_dir)) {
db->RecordUploadComplete(std::move(upload_report), std::string());
} else {
crashpad::Metrics::CrashUploadSkipped(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment