mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
linux: Add ExceptionHandlerServer and ExceptionHandlerClient
Bug: crashpad:30 Change-Id: I60874a26ccb281144f870df2b4d16c6970a39f6b Reviewed-on: https://chromium-review.googlesource.com/772824 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
99b0030616
commit
9b2ba587f6
37
compat/android/sys/epoll.cc
Normal file
37
compat/android/sys/epoll.cc
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if __ANDROID_API__ < 21
|
||||
|
||||
extern "C" {
|
||||
|
||||
int epoll_create1(int flags) {
|
||||
static const auto epoll_create1_p =
|
||||
reinterpret_cast<int (*)(int)>(dlsym(RTLD_DEFAULT, "epoll_create1"));
|
||||
if (epoll_create1_p) {
|
||||
return epoll_create1_p(flags);
|
||||
}
|
||||
|
||||
return syscall(SYS_epoll_create1, flags);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // __ANDROID_API__ < 21
|
50
compat/android/sys/epoll.h
Normal file
50
compat/android/sys/epoll.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_
|
||||
#define CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_
|
||||
|
||||
#include_next <sys/epoll.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// This is missing from traditional headers before API 21.
|
||||
#if !defined(EPOLLRDHUP)
|
||||
#define EPOLLRDHUP 0x00002000
|
||||
#endif
|
||||
|
||||
// EPOLL_CLOEXEC is undefined in traditional headers before API 21 and removed
|
||||
// from unified headers at API levels < 21 as a means to indicate that
|
||||
// epoll_create1 is missing from the C library, but the raw system call should
|
||||
// still be available.
|
||||
#if !defined(EPOLL_CLOEXEC)
|
||||
#define EPOLL_CLOEXEC O_CLOEXEC
|
||||
#endif
|
||||
|
||||
#if __ANDROID_API__ < 21
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int epoll_create1(int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __ANDROID_API__ < 21
|
||||
|
||||
#endif // CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_
|
@ -19,6 +19,10 @@
|
||||
|
||||
// Android 5.0.0 (API 21) NDK
|
||||
|
||||
#if !defined(SYS_epoll_create1)
|
||||
#define SYS_epoll_create1 __NR_epoll_create1
|
||||
#endif
|
||||
|
||||
#if !defined(SYS_gettid)
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
|
@ -26,6 +26,8 @@
|
||||
'android/linux/prctl.h',
|
||||
'android/linux/ptrace.h',
|
||||
'android/sched.h',
|
||||
'android/sys/epoll.cc',
|
||||
'android/sys/epoll.h',
|
||||
'android/sys/mman.cc',
|
||||
'android/sys/mman.h',
|
||||
'android/sys/syscall.h',
|
||||
|
@ -39,6 +39,8 @@
|
||||
'crash_report_upload_thread.h',
|
||||
'handler_main.cc',
|
||||
'handler_main.h',
|
||||
'linux/exception_handler_server.cc',
|
||||
'linux/exception_handler_server.h',
|
||||
'mac/crash_report_exception_handler.cc',
|
||||
'mac/crash_report_exception_handler.h',
|
||||
'mac/exception_handler_server.cc',
|
||||
@ -54,6 +56,27 @@
|
||||
'win/crash_report_exception_handler.cc',
|
||||
'win/crash_report_exception_handler.h',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="linux" or OS=="android"', {
|
||||
'sources!': [
|
||||
'handler_main.cc',
|
||||
],
|
||||
}],
|
||||
['OS=="linux"', {
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'-lcap',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
'target_conditions': [
|
||||
['OS=="android"', {
|
||||
'sources/': [
|
||||
['include', '^linux/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'crashpad_handler',
|
||||
|
@ -38,6 +38,7 @@
|
||||
],
|
||||
'sources': [
|
||||
'crashpad_handler_test.cc',
|
||||
'linux/exception_handler_server_test.cc',
|
||||
'minidump_to_upload_parameters_test.cc',
|
||||
],
|
||||
'conditions': [
|
||||
@ -50,6 +51,13 @@
|
||||
],
|
||||
}],
|
||||
],
|
||||
'target_conditions': [
|
||||
['OS=="android"', {
|
||||
'sources/': [
|
||||
['include', '^linux/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'crashpad_handler_test_extended_handler',
|
||||
|
446
handler/linux/exception_handler_server.cc
Normal file
446
handler/linux/exception_handler_server.cc
Normal file
@ -0,0 +1,446 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/linux/exception_handler_server.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/file/filesystem.h"
|
||||
#include "util/misc/as_underlying_type.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// Log an error for a socket after an EPOLLERR.
|
||||
void LogSocketError(int sock) {
|
||||
int err;
|
||||
socklen_t err_len = sizeof(err);
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) {
|
||||
PLOG(ERROR) << "getsockopt";
|
||||
} else {
|
||||
errno = err;
|
||||
PLOG(ERROR) << "EPOLLERR";
|
||||
}
|
||||
}
|
||||
|
||||
enum class PtraceScope {
|
||||
kClassic = 0,
|
||||
kRestricted,
|
||||
kAdminOnly,
|
||||
kNoAttach,
|
||||
kUnknown
|
||||
};
|
||||
|
||||
PtraceScope GetPtraceScope() {
|
||||
const base::FilePath settings_file("/proc/sys/kernel/yama/ptrace_scope");
|
||||
if (!IsRegularFile(base::FilePath(settings_file))) {
|
||||
return PtraceScope::kClassic;
|
||||
}
|
||||
|
||||
std::string contents;
|
||||
if (!LoggingReadEntireFile(settings_file, &contents)) {
|
||||
return PtraceScope::kUnknown;
|
||||
}
|
||||
|
||||
if (contents.back() != '\n') {
|
||||
LOG(ERROR) << "format error";
|
||||
return PtraceScope::kUnknown;
|
||||
}
|
||||
contents.pop_back();
|
||||
|
||||
int ptrace_scope;
|
||||
if (!base::StringToInt(contents, &ptrace_scope)) {
|
||||
LOG(ERROR) << "format error";
|
||||
return PtraceScope::kUnknown;
|
||||
}
|
||||
|
||||
if (ptrace_scope < static_cast<int>(PtraceScope::kClassic) ||
|
||||
ptrace_scope >= static_cast<int>(PtraceScope::kUnknown)) {
|
||||
LOG(ERROR) << "invalid ptrace scope";
|
||||
return PtraceScope::kUnknown;
|
||||
}
|
||||
|
||||
return static_cast<PtraceScope>(ptrace_scope);
|
||||
}
|
||||
|
||||
bool HaveCapSysPtrace() {
|
||||
struct __user_cap_header_struct cap_header = {};
|
||||
struct __user_cap_data_struct cap_data = {};
|
||||
|
||||
cap_header.pid = getpid();
|
||||
|
||||
if (capget(&cap_header, &cap_data) != 0) {
|
||||
PLOG(ERROR) << "capget";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cap_header.version != _LINUX_CAPABILITY_VERSION_3) {
|
||||
LOG(ERROR) << "Unexpected capability version " << std::hex
|
||||
<< cap_header.version;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (cap_data.effective & (1 << CAP_SYS_PTRACE)) != 0;
|
||||
}
|
||||
|
||||
bool SendMessageToClient(int client_sock, ServerToClientMessage::Type type) {
|
||||
ServerToClientMessage message = {};
|
||||
message.type = type;
|
||||
if (type == ServerToClientMessage::kTypeSetPtracer) {
|
||||
message.pid = getpid();
|
||||
}
|
||||
return LoggingWriteFile(client_sock, &message, sizeof(message));
|
||||
}
|
||||
|
||||
class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
||||
public:
|
||||
PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}
|
||||
~PtraceStrategyDeciderImpl() = default;
|
||||
|
||||
Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
|
||||
switch (GetPtraceScope()) {
|
||||
case PtraceScope::kClassic:
|
||||
return getuid() == client_credentials.uid ? Strategy::kDirectPtrace
|
||||
: Strategy::kForkBroker;
|
||||
|
||||
case PtraceScope::kRestricted:
|
||||
if (!SendMessageToClient(sock,
|
||||
ServerToClientMessage::kTypeSetPtracer)) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
|
||||
Errno status;
|
||||
if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
errno = status;
|
||||
PLOG(ERROR) << "Handler Client SetPtracer";
|
||||
return Strategy::kForkBroker;
|
||||
}
|
||||
return Strategy::kDirectPtrace;
|
||||
|
||||
case PtraceScope::kAdminOnly:
|
||||
if (HaveCapSysPtrace()) {
|
||||
return Strategy::kDirectPtrace;
|
||||
}
|
||||
// fallthrough
|
||||
case PtraceScope::kNoAttach:
|
||||
LOG(WARNING) << "no ptrace";
|
||||
return Strategy::kNoPtrace;
|
||||
|
||||
case PtraceScope::kUnknown:
|
||||
LOG(WARNING) << "Unknown ptrace scope";
|
||||
return Strategy::kError;
|
||||
}
|
||||
|
||||
DCHECK(false);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct ExceptionHandlerServer::Event {
|
||||
enum class Type { kShutdown, kClientMessage } type;
|
||||
|
||||
ScopedFileHandle fd;
|
||||
};
|
||||
|
||||
ExceptionHandlerServer::ExceptionHandlerServer()
|
||||
: clients_(),
|
||||
shutdown_event_(),
|
||||
strategy_decider_(new PtraceStrategyDeciderImpl()),
|
||||
delegate_(nullptr),
|
||||
pollfd_(),
|
||||
keep_running_(true) {}
|
||||
|
||||
ExceptionHandlerServer::~ExceptionHandlerServer() = default;
|
||||
|
||||
void ExceptionHandlerServer::SetPtraceStrategyDecider(
|
||||
std::unique_ptr<PtraceStrategyDecider> decider) {
|
||||
strategy_decider_ = std::move(decider);
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||
if (!pollfd_.is_valid()) {
|
||||
PLOG(ERROR) << "epoll_create1";
|
||||
return false;
|
||||
}
|
||||
|
||||
shutdown_event_ = std::make_unique<Event>();
|
||||
shutdown_event_->type = Event::Type::kShutdown;
|
||||
shutdown_event_->fd.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
|
||||
if (!shutdown_event_->fd.is_valid()) {
|
||||
PLOG(ERROR) << "eventfd";
|
||||
return false;
|
||||
}
|
||||
|
||||
epoll_event poll_event;
|
||||
poll_event.events = EPOLLIN;
|
||||
poll_event.data.ptr = shutdown_event_.get();
|
||||
if (epoll_ctl(pollfd_.get(),
|
||||
EPOLL_CTL_ADD,
|
||||
shutdown_event_->fd.get(),
|
||||
&poll_event) != 0) {
|
||||
PLOG(ERROR) << "epoll_ctl";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InstallClientSocket(std::move(sock))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExceptionHandlerServer::Run(Delegate* delegate) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
delegate_ = delegate;
|
||||
|
||||
while (keep_running_ && clients_.size() > 0) {
|
||||
epoll_event poll_event;
|
||||
int res = HANDLE_EINTR(epoll_wait(pollfd_.get(), &poll_event, 1, -1));
|
||||
if (res < 0) {
|
||||
PLOG(ERROR) << "epoll_wait";
|
||||
return;
|
||||
}
|
||||
DCHECK_EQ(res, 1);
|
||||
|
||||
Event* eventp = reinterpret_cast<Event*>(poll_event.data.ptr);
|
||||
if (eventp->type == Event::Type::kShutdown) {
|
||||
if (poll_event.events & EPOLLERR) {
|
||||
LogSocketError(eventp->fd.get());
|
||||
}
|
||||
keep_running_ = false;
|
||||
} else {
|
||||
HandleEvent(eventp, poll_event.events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandlerServer::Stop() {
|
||||
keep_running_ = false;
|
||||
if (shutdown_event_ && shutdown_event_->fd.is_valid()) {
|
||||
uint64_t value = 1;
|
||||
LoggingWriteFile(shutdown_event_->fd.get(), &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
||||
DCHECK_EQ(AsUnderlyingType(event->type),
|
||||
AsUnderlyingType(Event::Type::kClientMessage));
|
||||
|
||||
if (event_type & EPOLLERR) {
|
||||
LogSocketError(event->fd.get());
|
||||
UninstallClientSocket(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_type & EPOLLIN) {
|
||||
if (!ReceiveClientMessage(event)) {
|
||||
UninstallClientSocket(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_type & EPOLLHUP || event_type & EPOLLRDHUP) {
|
||||
UninstallClientSocket(event);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Unexpected event 0x" << std::hex << event_type;
|
||||
return;
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket) {
|
||||
int optval = 1;
|
||||
socklen_t optlen = sizeof(optval);
|
||||
if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != 0) {
|
||||
PLOG(ERROR) << "setsockopt";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto event = std::make_unique<Event>();
|
||||
event->type = Event::Type::kClientMessage;
|
||||
event->fd.reset(socket.release());
|
||||
|
||||
Event* eventp = event.get();
|
||||
|
||||
if (!clients_.insert(std::make_pair(event->fd.get(), std::move(event)))
|
||||
.second) {
|
||||
LOG(ERROR) << "duplicate descriptor";
|
||||
return false;
|
||||
}
|
||||
|
||||
epoll_event poll_event;
|
||||
poll_event.events = EPOLLIN | EPOLLRDHUP;
|
||||
poll_event.data.ptr = eventp;
|
||||
|
||||
if (epoll_ctl(pollfd_.get(), EPOLL_CTL_ADD, eventp->fd.get(), &poll_event) !=
|
||||
0) {
|
||||
PLOG(ERROR) << "epoll_ctl";
|
||||
clients_.erase(eventp->fd.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::UninstallClientSocket(Event* event) {
|
||||
if (epoll_ctl(pollfd_.get(), EPOLL_CTL_DEL, event->fd.get(), nullptr) != 0) {
|
||||
PLOG(ERROR) << "epoll_ctl";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clients_.erase(event->fd.get()) != 1) {
|
||||
LOG(ERROR) << "event not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
||||
ClientToServerMessage message;
|
||||
iovec iov;
|
||||
iov.iov_base = &message;
|
||||
iov.iov_len = sizeof(message);
|
||||
|
||||
msghdr msg;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
char cmsg_buf[CMSG_SPACE(sizeof(ucred))];
|
||||
msg.msg_control = cmsg_buf;
|
||||
msg.msg_controllen = sizeof(cmsg_buf);
|
||||
msg.msg_flags = 0;
|
||||
|
||||
int res = recvmsg(event->fd.get(), &msg, 0);
|
||||
if (res < 0) {
|
||||
PLOG(ERROR) << "recvmsg";
|
||||
return false;
|
||||
}
|
||||
if (res == 0) {
|
||||
// The client had an orderly shutdown.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.msg_name != nullptr || msg.msg_namelen != 0) {
|
||||
LOG(ERROR) << "unexpected msg name";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.msg_iovlen != 1) {
|
||||
LOG(ERROR) << "unexpected iovlen";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.msg_iov[0].iov_len != sizeof(ClientToServerMessage)) {
|
||||
LOG(ERROR) << "unexpected message size " << msg.msg_iov[0].iov_len;
|
||||
return false;
|
||||
}
|
||||
auto client_msg =
|
||||
reinterpret_cast<ClientToServerMessage*>(msg.msg_iov[0].iov_base);
|
||||
|
||||
switch (client_msg->type) {
|
||||
case ClientToServerMessage::kCrashDumpRequest:
|
||||
return HandleCrashDumpRequest(
|
||||
msg, client_msg->client_info, event->fd.get());
|
||||
}
|
||||
|
||||
DCHECK(false);
|
||||
LOG(ERROR) << "Unknown message type";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::HandleCrashDumpRequest(
|
||||
const msghdr& msg,
|
||||
const ClientInformation& client_info,
|
||||
int client_sock) {
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg == nullptr) {
|
||||
LOG(ERROR) << "missing credentials";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
LOG(ERROR) << "unexpected cmsg_level " << cmsg->cmsg_level;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_type != SCM_CREDENTIALS) {
|
||||
LOG(ERROR) << "unexpected cmsg_type " << cmsg->cmsg_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(ucred))) {
|
||||
LOG(ERROR) << "unexpected cmsg_len " << cmsg->cmsg_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
ucred* client_credentials = reinterpret_cast<ucred*>(CMSG_DATA(cmsg));
|
||||
pid_t client_process_id = client_credentials->pid;
|
||||
|
||||
switch (strategy_decider_->ChooseStrategy(client_sock, *client_credentials)) {
|
||||
case PtraceStrategyDecider::Strategy::kError:
|
||||
return false;
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kNoPtrace:
|
||||
return SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeCrashDumpFailed);
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kDirectPtrace:
|
||||
delegate_->HandleException(client_process_id,
|
||||
client_info.exception_information_address);
|
||||
break;
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kForkBroker:
|
||||
if (!SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeForkBroker)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delegate_->HandleExceptionWithBroker(
|
||||
client_process_id,
|
||||
client_info.exception_information_address,
|
||||
client_sock);
|
||||
break;
|
||||
}
|
||||
|
||||
return SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeCrashDumpComplete);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
155
handler/linux/exception_handler_server.h
Normal file
155
handler/linux/exception_handler_server.h
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_
|
||||
#define CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Abstract base class for deciding how the handler should `ptrace` a
|
||||
//! client.
|
||||
class PtraceStrategyDecider {
|
||||
public:
|
||||
virtual ~PtraceStrategyDecider() = default;
|
||||
|
||||
//! \brief The possible return values for ChooseStrategy().
|
||||
enum class Strategy {
|
||||
//! \brief An error occurred, with a message logged.
|
||||
kError,
|
||||
|
||||
//! \brief Ptrace cannot be used.
|
||||
kNoPtrace,
|
||||
|
||||
//! \brief The handler should `ptrace`-attach the client directly.
|
||||
kDirectPtrace,
|
||||
|
||||
//! \brief The client should `fork` a PtraceBroker for the handler.
|
||||
kForkBroker,
|
||||
};
|
||||
|
||||
//! \brief Chooses an appropriate `ptrace` strategy.
|
||||
//!
|
||||
//! \param[in] sock A socket conncted to a ExceptionHandlerClient.
|
||||
//! \param[in] client_credentials The credentials for the connected client.
|
||||
//! \return the chosen #Strategy.
|
||||
virtual Strategy ChooseStrategy(int sock,
|
||||
const ucred& client_credentials) = 0;
|
||||
|
||||
protected:
|
||||
PtraceStrategyDecider() = default;
|
||||
};
|
||||
|
||||
//! \brief Runs the main exception-handling server in Crashpad’s handler
|
||||
//! process.
|
||||
class ExceptionHandlerServer {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
//! \brief Called on receipt of a crash dump request from a client.
|
||||
//!
|
||||
//! \param[in] client_process_id The process ID of the crashing client.
|
||||
//! \param[in] exception_information_address The address in the client's
|
||||
//! address space of an ExceptionInformation struct.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool HandleException(pid_t client_process_id,
|
||||
VMAddress exception_information_address) = 0;
|
||||
|
||||
//! \brief Called on the receipt of a crash dump request from a client for a
|
||||
//! crash that should be mediated by a PtraceBroker.
|
||||
//!
|
||||
//! \param[in] client_process_id The process ID of the crashing client.
|
||||
//! \param[in] exception_information_address The address in the client's
|
||||
//! address space of an ExceptionInformation struct.
|
||||
//! \param[in] broker_sock A socket connected to the PtraceBroker.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
VMAddress exception_information_address,
|
||||
int broker_sock) = 0;
|
||||
|
||||
protected:
|
||||
~Delegate() {}
|
||||
};
|
||||
|
||||
ExceptionHandlerServer();
|
||||
~ExceptionHandlerServer();
|
||||
|
||||
//! \brief Sets the handler's PtraceStrategyDecider.
|
||||
//!
|
||||
//! If this method is not called, a default PtraceStrategyDecider will be
|
||||
//! used.
|
||||
void SetPtraceStrategyDecider(std::unique_ptr<PtraceStrategyDecider> decider);
|
||||
|
||||
//! \brief Initializes this object.
|
||||
//!
|
||||
//! This method must be successfully called before Run().
|
||||
//!
|
||||
//! \param[in] sock A socket on which to receive client requests.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
bool InitializeWithClient(ScopedFileHandle sock);
|
||||
|
||||
//! \brief Runs the exception-handling server.
|
||||
//!
|
||||
//! This method must only be called once on an ExceptionHandlerServer object.
|
||||
//! This method returns when there are no more client connections or Stop()
|
||||
//! has been called.
|
||||
//!
|
||||
//! \param[in] delegate An object to send exceptions to.
|
||||
void Run(Delegate* delegate);
|
||||
|
||||
//! \brief Stops a running exception-handling server.
|
||||
//!
|
||||
//! Stop() may be called at any time, and may be called from a signal handler.
|
||||
//! If Stop() is called before Run() it will cause Run() to return as soon as
|
||||
//! it is called. It is harmless to call Stop() after Run() has already
|
||||
//! returned, or to call Stop() after it has already been called.
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
struct Event;
|
||||
|
||||
void HandleEvent(Event* event, uint32_t event_type);
|
||||
bool InstallClientSocket(ScopedFileHandle socket);
|
||||
bool UninstallClientSocket(Event* event);
|
||||
bool ReceiveClientMessage(Event* event);
|
||||
bool HandleCrashDumpRequest(const msghdr& msg,
|
||||
const ClientInformation& client_info,
|
||||
int client_sock);
|
||||
|
||||
std::unordered_map<int, std::unique_ptr<Event>> clients_;
|
||||
std::unique_ptr<Event> shutdown_event_;
|
||||
std::unique_ptr<PtraceStrategyDecider> strategy_decider_;
|
||||
Delegate* delegate_;
|
||||
ScopedFileHandle pollfd_;
|
||||
bool keep_running_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_
|
307
handler/linux/exception_handler_server_test.cc
Normal file
307
handler/linux/exception_handler_server_test.cc
Normal file
@ -0,0 +1,307 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/linux/exception_handler_server.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/linux/scoped_pr_set_ptracer.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/linux/exception_handler_client.h"
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
// Runs the ExceptionHandlerServer on a background thread.
|
||||
class RunServerThread : public Thread {
|
||||
public:
|
||||
RunServerThread(ExceptionHandlerServer* server,
|
||||
ExceptionHandlerServer::Delegate* delegate)
|
||||
: server_(server), delegate_(delegate), join_sem_(0) {}
|
||||
|
||||
~RunServerThread() override {}
|
||||
|
||||
bool JoinWithTimeout(double timeout) {
|
||||
if (!join_sem_.TimedWait(timeout)) {
|
||||
return false;
|
||||
}
|
||||
Join();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Thread:
|
||||
void ThreadMain() override {
|
||||
server_->Run(delegate_);
|
||||
join_sem_.Signal();
|
||||
}
|
||||
|
||||
ExceptionHandlerServer* server_;
|
||||
ExceptionHandlerServer::Delegate* delegate_;
|
||||
Semaphore join_sem_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RunServerThread);
|
||||
};
|
||||
|
||||
class ScopedStopServerAndJoinThread {
|
||||
public:
|
||||
ScopedStopServerAndJoinThread(ExceptionHandlerServer* server,
|
||||
RunServerThread* thread)
|
||||
: server_(server), thread_(thread) {}
|
||||
|
||||
~ScopedStopServerAndJoinThread() {
|
||||
server_->Stop();
|
||||
EXPECT_TRUE(thread_->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
private:
|
||||
ExceptionHandlerServer* server_;
|
||||
RunServerThread* thread_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread);
|
||||
};
|
||||
|
||||
class TestDelegate : public ExceptionHandlerServer::Delegate {
|
||||
public:
|
||||
TestDelegate()
|
||||
: Delegate(), last_exception_address_(0), last_client_(-1), sem_(0) {}
|
||||
|
||||
~TestDelegate() {}
|
||||
|
||||
bool WaitForException(double timeout_seconds,
|
||||
pid_t* last_client,
|
||||
VMAddress* last_address) {
|
||||
if (sem_.TimedWait(timeout_seconds)) {
|
||||
*last_client = last_client_;
|
||||
*last_address = last_exception_address_;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleException(pid_t client_process_id,
|
||||
VMAddress exception_information_address) override {
|
||||
DirectPtraceConnection connection;
|
||||
bool connected = connection.Initialize(client_process_id);
|
||||
EXPECT_TRUE(connected);
|
||||
|
||||
last_exception_address_ = exception_information_address;
|
||||
last_client_ = client_process_id;
|
||||
sem_.Signal();
|
||||
return connected;
|
||||
}
|
||||
|
||||
bool HandleExceptionWithBroker(pid_t client_process_id,
|
||||
VMAddress exception_information_address,
|
||||
int broker_sock) override {
|
||||
PtraceClient client;
|
||||
bool connected = client.Initialize(broker_sock, client_process_id);
|
||||
EXPECT_TRUE(connected);
|
||||
|
||||
last_exception_address_ = exception_information_address,
|
||||
last_client_ = client_process_id;
|
||||
sem_.Signal();
|
||||
return connected;
|
||||
}
|
||||
|
||||
private:
|
||||
VMAddress last_exception_address_;
|
||||
pid_t last_client_;
|
||||
Semaphore sem_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
|
||||
};
|
||||
|
||||
class MockPtraceStrategyDecider : public PtraceStrategyDecider {
|
||||
public:
|
||||
MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy)
|
||||
: PtraceStrategyDecider(), strategy_(strategy) {}
|
||||
|
||||
~MockPtraceStrategyDecider() {}
|
||||
|
||||
Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
|
||||
return strategy_;
|
||||
}
|
||||
|
||||
private:
|
||||
Strategy strategy_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
|
||||
};
|
||||
|
||||
class ExceptionHandlerServerTest : public testing::Test {
|
||||
public:
|
||||
ExceptionHandlerServerTest()
|
||||
: server_(),
|
||||
delegate_(),
|
||||
server_thread_(&server_, &delegate_),
|
||||
sock_to_handler_() {}
|
||||
|
||||
~ExceptionHandlerServerTest() = default;
|
||||
|
||||
int SockToHandler() { return sock_to_handler_.get(); }
|
||||
|
||||
TestDelegate* Delegate() { return &delegate_; }
|
||||
|
||||
void Hangup() { sock_to_handler_.reset(); }
|
||||
|
||||
RunServerThread* ServerThread() { return &server_thread_; }
|
||||
|
||||
ExceptionHandlerServer* Server() { return &server_; }
|
||||
|
||||
class CrashDumpTest : public Multiprocess {
|
||||
public:
|
||||
CrashDumpTest(ExceptionHandlerServerTest* server_test, bool succeeds)
|
||||
: Multiprocess(), server_test_(server_test), succeeds_(succeeds) {}
|
||||
|
||||
~CrashDumpTest() = default;
|
||||
|
||||
void MultiprocessParent() override {
|
||||
ClientInformation info;
|
||||
ASSERT_TRUE(
|
||||
LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
|
||||
|
||||
if (succeeds_) {
|
||||
VMAddress last_address;
|
||||
pid_t last_client;
|
||||
ASSERT_TRUE(server_test_->Delegate()->WaitForException(
|
||||
5.0, &last_client, &last_address));
|
||||
EXPECT_EQ(last_address, info.exception_information_address);
|
||||
EXPECT_EQ(last_client, ChildPID());
|
||||
} else {
|
||||
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiprocessChild() override {
|
||||
ASSERT_EQ(close(server_test_->sock_to_client_), 0);
|
||||
|
||||
ClientInformation info;
|
||||
info.exception_information_address = 42;
|
||||
|
||||
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
|
||||
|
||||
// If the current ptrace_scope is restricted, the broker needs to be set
|
||||
// as the ptracer for this process. Setting this process as its own
|
||||
// ptracer allows the broker to inherit this condition.
|
||||
ScopedPrSetPtracer set_ptracer(getpid());
|
||||
|
||||
ExceptionHandlerClient client(server_test_->SockToHandler());
|
||||
ASSERT_EQ(client.RequestCrashDump(info), 0);
|
||||
}
|
||||
|
||||
private:
|
||||
ExceptionHandlerServerTest* server_test_;
|
||||
bool succeeds_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashDumpTest);
|
||||
};
|
||||
|
||||
void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
|
||||
bool succeeds) {
|
||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||
ServerThread()->Start();
|
||||
|
||||
Server()->SetPtraceStrategyDecider(
|
||||
std::make_unique<MockPtraceStrategyDecider>(strategy));
|
||||
|
||||
CrashDumpTest test(this, succeeds);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
int socks[2];
|
||||
ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0);
|
||||
sock_to_handler_.reset(socks[0]);
|
||||
sock_to_client_ = socks[1];
|
||||
|
||||
ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1])));
|
||||
}
|
||||
|
||||
private:
|
||||
ExceptionHandlerServer server_;
|
||||
TestDelegate delegate_;
|
||||
RunServerThread server_thread_;
|
||||
ScopedFileHandle sock_to_handler_;
|
||||
int sock_to_client_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
|
||||
};
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, ShutdownWithNoClients) {
|
||||
ServerThread()->Start();
|
||||
Hangup();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, StopWithClients) {
|
||||
ServerThread()->Start();
|
||||
Server()->Stop();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, StopBeforeRun) {
|
||||
Server()->Stop();
|
||||
ServerThread()->Start();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, MultipleStops) {
|
||||
ServerThread()->Start();
|
||||
Server()->Stop();
|
||||
Server()->Stop();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||
ServerThread()->Start();
|
||||
|
||||
CrashDumpTest test(this, true);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kForkBroker,
|
||||
true);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
|
||||
true);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpError) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -63,9 +63,7 @@ class ExceptionHandlerServer {
|
||||
|
||||
//! \brief Stops a running exception-handling server.
|
||||
//!
|
||||
//! The normal mode of operation is to call Stop() while Run() is running. It
|
||||
//! is expected that Stop() would be called from a signal handler.
|
||||
//!
|
||||
//! Stop() may be called at any time, and may be called from a signal handler.
|
||||
//! If Stop() is called before Run() it will cause Run() to return as soon as
|
||||
//! it is called. It is harmless to call Stop() after Run() has already
|
||||
//! returned, or to call Stop() after it has already been called.
|
||||
|
@ -58,7 +58,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
const UserStreamDataSources* user_stream_data_sources);
|
||||
|
||||
~CrashReportExceptionHandler() override;
|
||||
~CrashReportExceptionHandler();
|
||||
|
||||
// ExceptionHandlerServer::Delegate:
|
||||
|
||||
|
@ -53,7 +53,7 @@ void InitializeCPUContextX86_NoFloatingPoint(
|
||||
const SignalThreadContext32& thread_context,
|
||||
CPUContextX86* context);
|
||||
|
||||
// \{
|
||||
//! \{
|
||||
//! \brief Initializes a CPUContextX86_64 structure from native context
|
||||
//! structures on Linux.
|
||||
//!
|
||||
|
@ -79,7 +79,7 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate {
|
||||
: server_ready_(server_ready),
|
||||
completed_test_event_(completed_test_event),
|
||||
break_near_(0) {}
|
||||
~CrashingDelegate() override {}
|
||||
~CrashingDelegate() {}
|
||||
|
||||
void set_break_near(WinVMAddress break_near) { break_near_ = break_near; }
|
||||
|
||||
@ -183,7 +183,7 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate {
|
||||
: server_ready_(server_ready),
|
||||
completed_test_event_(completed_test_event),
|
||||
dump_near_(0) {}
|
||||
~SimulateDelegate() override {}
|
||||
~SimulateDelegate() {}
|
||||
|
||||
void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; }
|
||||
|
||||
|
40
test/linux/scoped_pr_set_ptracer.cc
Normal file
40
test/linux/scoped_pr_set_ptracer.cc
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "test/linux/scoped_pr_set_ptracer.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
ScopedPrSetPtracer::ScopedPrSetPtracer(pid_t pid) {
|
||||
success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0;
|
||||
if (!success_) {
|
||||
EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl");
|
||||
}
|
||||
}
|
||||
|
||||
ScopedPrSetPtracer::~ScopedPrSetPtracer() {
|
||||
if (success_) {
|
||||
EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
47
test/linux/scoped_pr_set_ptracer.h
Normal file
47
test/linux/scoped_pr_set_ptracer.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_
|
||||
#define CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
|
||||
class ScopedPrSetPtracer {
|
||||
public:
|
||||
//! \brief Uses `PR_SET_PTRACER` to set \a pid as the caller's ptracer or
|
||||
//! expects `EINVAL`.
|
||||
//!
|
||||
//! `PR_SET_PTRACER` is only supported if the Yama Linux security module (LSM)
|
||||
//! is enabled. Otherwise, `prctl(PR_SET_PTRACER, ...)` fails with `EINVAL`.
|
||||
//! See linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and
|
||||
//! linux-4.9.20/kernel/sys.c [sys_]prctl().
|
||||
explicit ScopedPrSetPtracer(pid_t pid);
|
||||
|
||||
~ScopedPrSetPtracer();
|
||||
|
||||
private:
|
||||
bool success_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_
|
@ -45,6 +45,8 @@
|
||||
'linux/fake_ptrace_connection.h',
|
||||
'linux/get_tls.cc',
|
||||
'linux/get_tls.h',
|
||||
'linux/scoped_pr_set_ptracer.cc',
|
||||
'linux/scoped_pr_set_ptracer.h',
|
||||
'mac/dyld.cc',
|
||||
'mac/dyld.h',
|
||||
'mac/exception_swallower.cc',
|
||||
|
162
util/linux/exception_handler_client.cc
Normal file
162
util/linux/exception_handler_client.cc
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/linux/exception_handler_client.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/linux/ptrace_broker.h"
|
||||
#include "util/posix/signals.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
ExceptionHandlerClient::ExceptionHandlerClient(int sock)
|
||||
: server_sock_(sock), ptracer_(-1), can_set_ptracer_(true) {}
|
||||
|
||||
ExceptionHandlerClient::~ExceptionHandlerClient() = default;
|
||||
|
||||
int ExceptionHandlerClient::RequestCrashDump(const ClientInformation& info) {
|
||||
int status = SendCrashDumpRequest(info);
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
return WaitForCrashDumpComplete();
|
||||
}
|
||||
|
||||
int ExceptionHandlerClient::SetPtracer(pid_t pid) {
|
||||
if (ptracer_ == pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!can_set_ptracer_) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0) {
|
||||
return 0;
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
|
||||
void ExceptionHandlerClient::SetCanSetPtracer(bool can_set_ptracer) {
|
||||
can_set_ptracer_ = can_set_ptracer;
|
||||
}
|
||||
|
||||
int ExceptionHandlerClient::SendCrashDumpRequest(
|
||||
const ClientInformation& info) {
|
||||
ClientToServerMessage message;
|
||||
message.type = ClientToServerMessage::kCrashDumpRequest;
|
||||
message.client_info = info;
|
||||
|
||||
iovec iov;
|
||||
iov.iov_base = &message;
|
||||
iov.iov_len = sizeof(message);
|
||||
|
||||
msghdr msg;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ucred creds;
|
||||
creds.pid = getpid();
|
||||
creds.uid = geteuid();
|
||||
creds.gid = getegid();
|
||||
|
||||
char cmsg_buf[CMSG_SPACE(sizeof(creds))];
|
||||
msg.msg_control = cmsg_buf;
|
||||
msg.msg_controllen = sizeof(cmsg_buf);
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_CREDENTIALS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(creds));
|
||||
*reinterpret_cast<ucred*>(CMSG_DATA(cmsg)) = creds;
|
||||
|
||||
if (sendmsg(server_sock_, &msg, MSG_NOSIGNAL) < 0) {
|
||||
PLOG(ERROR) << "sendmsg";
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExceptionHandlerClient::WaitForCrashDumpComplete() {
|
||||
ServerToClientMessage message;
|
||||
|
||||
// If the server hangs up, ReadFileExactly will return false without setting
|
||||
// errno.
|
||||
errno = 0;
|
||||
while (ReadFileExactly(server_sock_, &message, sizeof(message))) {
|
||||
switch (message.type) {
|
||||
case ServerToClientMessage::kTypeForkBroker: {
|
||||
Signals::InstallDefaultHandler(SIGCHLD);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
Errno error = errno;
|
||||
if (!WriteFile(server_sock_, &error, sizeof(error))) {
|
||||
return errno;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
constexpr bool am_64_bit = true;
|
||||
#else
|
||||
constexpr bool am_64_bit = false;
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
PtraceBroker broker(server_sock_, am_64_bit);
|
||||
_exit(broker.Run());
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pid_t child = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
DCHECK_EQ(child, pid);
|
||||
|
||||
if (child == pid && status != 0) {
|
||||
return status;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case ServerToClientMessage::kTypeSetPtracer: {
|
||||
Errno result = SetPtracer(message.pid);
|
||||
if (!WriteFile(server_sock_, &result, sizeof(result))) {
|
||||
return errno;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case ServerToClientMessage::kTypeCrashDumpComplete:
|
||||
case ServerToClientMessage::kTypeCrashDumpFailed:
|
||||
return 0;
|
||||
}
|
||||
|
||||
DCHECK(false);
|
||||
}
|
||||
|
||||
return errno;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
68
util/linux/exception_handler_client.h
Normal file
68
util/linux/exception_handler_client.h
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_
|
||||
#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! A client for an ExceptionHandlerServer
|
||||
class ExceptionHandlerClient {
|
||||
public:
|
||||
//! \brief Constructs this object.
|
||||
//!
|
||||
//! \param[in] sock A socket connected to an ExceptionHandlerServer.
|
||||
explicit ExceptionHandlerClient(int sock);
|
||||
|
||||
~ExceptionHandlerClient();
|
||||
|
||||
//! \brief Request a crash dump from the ExceptionHandlerServer.
|
||||
//!
|
||||
//! This method blocks until the crash dump is complete.
|
||||
//!
|
||||
//! \param[in] info Information about this client.
|
||||
//! \return 0 on success or an error code on failure.
|
||||
int RequestCrashDump(const ClientInformation& info);
|
||||
|
||||
//! \brief Uses `prctl(PR_SET_PTRACER, ...)` to set the process with
|
||||
//! process ID \a pid as the ptracer for this process.
|
||||
//!
|
||||
//! \param[in] pid The process ID of the process to be set as this process'
|
||||
//! ptracer.
|
||||
//! \return 0 on success or an error code on failure.
|
||||
int SetPtracer(pid_t pid);
|
||||
|
||||
//! \brief Enables or disables SetPtracer().
|
||||
//! \param[in] can_set_ptracer Whether SetPtracer should be enabled.
|
||||
void SetCanSetPtracer(bool can_set_ptracer);
|
||||
|
||||
private:
|
||||
int SendCrashDumpRequest(const ClientInformation& info);
|
||||
int WaitForCrashDumpComplete();
|
||||
|
||||
int server_sock_;
|
||||
pid_t ptracer_;
|
||||
bool can_set_ptracer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerClient);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_
|
86
util/linux/exception_handler_protocol.h
Normal file
86
util/linux/exception_handler_protocol.h
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_
|
||||
#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/misc/address_types.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
//! \brief The type used for error reporting.
|
||||
using Errno = int32_t;
|
||||
static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small");
|
||||
|
||||
//! \brief A boolean status suitable for communication between processes.
|
||||
enum Bool : char { kBoolFalse, kBoolTrue };
|
||||
|
||||
//! \brief Information about a client registered with an ExceptionHandlerServer.
|
||||
struct ClientInformation {
|
||||
//! \brief The address in the client's address space of an
|
||||
//! ExceptionInformation struct.
|
||||
VMAddress exception_information_address;
|
||||
};
|
||||
|
||||
//! \brief The message passed from client to server.
|
||||
struct ClientToServerMessage {
|
||||
static constexpr int32_t kVersion = 1;
|
||||
|
||||
//! \brief Indicates what message version is being used.
|
||||
int32_t version = kVersion;
|
||||
|
||||
enum Type : uint32_t {
|
||||
//! \brief Used to request a crash dump for the sending client.
|
||||
kCrashDumpRequest
|
||||
} type;
|
||||
|
||||
union {
|
||||
//! \brief Valid for type == kCrashDumpRequest
|
||||
ClientInformation client_info;
|
||||
};
|
||||
};
|
||||
|
||||
//! \brief The message passed from server to client.
|
||||
struct ServerToClientMessage {
|
||||
enum Type : uint32_t {
|
||||
//! \brief Indicates that the client should fork a PtraceBroker process.
|
||||
kTypeForkBroker,
|
||||
|
||||
//! \brief Inidicates that the client should set allow the handler to trace
|
||||
//! it using PR_SET_PTRACER.
|
||||
kTypeSetPtracer,
|
||||
|
||||
//! \brief Indicates that the handler has completed a requested crash dump.
|
||||
kTypeCrashDumpComplete,
|
||||
|
||||
//! \brief Indicicates that the handler was unable to produce a crash dump.
|
||||
kTypeCrashDumpFailed
|
||||
} type;
|
||||
|
||||
//! \brief The handler's process ID. Valid for kTypeSetPtracer.
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_
|
@ -20,6 +20,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/linux/ptracer.h"
|
||||
#include "util/linux/scoped_ptrace_attach.h"
|
||||
@ -87,13 +88,6 @@ class PtraceBroker {
|
||||
} iov;
|
||||
};
|
||||
|
||||
//! \brief The type used for error reporting.
|
||||
using Errno = int32_t;
|
||||
static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small");
|
||||
|
||||
//! \brief A boolean status suitable for communication between processes.
|
||||
enum Bool : char { kBoolFalse, kBoolTrue };
|
||||
|
||||
//! \brief The response sent for a Request with type kTypeGetThreadInfo.
|
||||
struct GetThreadInfoResponse {
|
||||
//! \brief Information about the specified thread. Only valid if #success
|
||||
|
@ -27,7 +27,7 @@ namespace crashpad {
|
||||
namespace {
|
||||
|
||||
bool ReceiveAndLogError(int sock, const std::string& operation) {
|
||||
PtraceBroker::Errno error;
|
||||
Errno error;
|
||||
if (!LoggingReadFileExactly(sock, &error, sizeof(error))) {
|
||||
return false;
|
||||
}
|
||||
@ -44,13 +44,14 @@ bool AttachImpl(int sock, pid_t tid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PtraceBroker::Bool success;
|
||||
Bool success;
|
||||
if (!LoggingReadFileExactly(sock, &success, sizeof(success))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (success != PtraceBroker::kBoolTrue) {
|
||||
if (success != kBoolTrue) {
|
||||
ReceiveAndLogError(sock, "PtraceBroker Attach");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -90,11 +91,11 @@ bool PtraceClient::Initialize(int sock, pid_t pid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PtraceBroker::Bool is_64_bit;
|
||||
Bool is_64_bit;
|
||||
if (!LoggingReadFileExactly(sock_, &is_64_bit, sizeof(is_64_bit))) {
|
||||
return false;
|
||||
}
|
||||
is_64_bit_ = is_64_bit == PtraceBroker::kBoolTrue;
|
||||
is_64_bit_ = is_64_bit == kBoolTrue;
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
@ -130,7 +131,7 @@ bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.success == PtraceBroker::kBoolTrue) {
|
||||
if (response.success == kBoolTrue) {
|
||||
*info = response.info;
|
||||
return true;
|
||||
}
|
||||
|
@ -15,12 +15,12 @@
|
||||
#include "util/linux/scoped_ptrace_attach.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/linux/scoped_pr_set_ptracer.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
@ -28,39 +28,13 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
class ScopedPrSetPtracer {
|
||||
public:
|
||||
explicit ScopedPrSetPtracer(pid_t pid) {
|
||||
// PR_SET_PTRACER is only supported if the Yama Linux security module (LSM)
|
||||
// is enabled. Otherwise, this prctl() call fails with EINVAL. See
|
||||
// linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and
|
||||
// linux-4.9.20/kernel/sys.c [sys_]prctl().
|
||||
//
|
||||
// If Yama is not enabled, the default ptrace restrictions should be
|
||||
// sufficient for these tests.
|
||||
//
|
||||
// If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0
|
||||
// (YAMA_SCOPE_DISABLED, in which case this prctl() is not necessary) or 1
|
||||
// (YAMA_SCOPE_RELATIONAL) for these tests to succeed. If it is 2
|
||||
// (YAMA_SCOPE_CAPABILITY) then the test requires CAP_SYS_PTRACE, and if it
|
||||
// is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail.
|
||||
success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0;
|
||||
if (!success_) {
|
||||
EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl");
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedPrSetPtracer() {
|
||||
if (success_) {
|
||||
EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool success_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer);
|
||||
};
|
||||
// If Yama is not enabled, the default ptrace restrictions should be
|
||||
// sufficient for these tests.
|
||||
//
|
||||
// If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0
|
||||
// (YAMA_SCOPE_DISABLED) or 1 (YAMA_SCOPE_RELATIONAL) for these tests to
|
||||
// succeed. If it is 2 (YAMA_SCOPE_CAPABILITY) then the test requires
|
||||
// CAP_SYS_PTRACE, and if it is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail.
|
||||
|
||||
class AttachTest : public Multiprocess {
|
||||
public:
|
||||
|
@ -138,6 +138,15 @@ bool Signals::InstallHandler(int sig,
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::InstallDefaultHandler(int sig) {
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = SIG_DFL;
|
||||
return sigaction(sig, &action, nullptr) == 0;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Signals::InstallCrashHandlers(Handler handler,
|
||||
int flags,
|
||||
|
@ -85,6 +85,14 @@ class Signals {
|
||||
int flags,
|
||||
struct sigaction* old_action);
|
||||
|
||||
//! \brief Installs `SIG_DFL` for the signal \a sig.
|
||||
//!
|
||||
//! \param[in] sig The signal to set the default action for.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with errno set. No message
|
||||
//! is logged.
|
||||
static bool InstallDefaultHandler(int sig);
|
||||
|
||||
//! \brief Installs a new signal handler for all signals associated with
|
||||
//! crashes.
|
||||
//!
|
||||
|
@ -58,6 +58,10 @@
|
||||
'linux/checked_address_range.h',
|
||||
'linux/direct_ptrace_connection.cc',
|
||||
'linux/direct_ptrace_connection.h',
|
||||
'linux/exception_handler_client.cc',
|
||||
'linux/exception_handler_client.h',
|
||||
'linux/exception_handler_protocol.h',
|
||||
'linux/exception_information.h',
|
||||
'linux/memory_map.cc',
|
||||
'linux/memory_map.h',
|
||||
'linux/proc_stat_reader.cc',
|
||||
@ -69,7 +73,6 @@
|
||||
'linux/ptrace_connection.h',
|
||||
'linux/ptracer.cc',
|
||||
'linux/ptracer.h',
|
||||
'linux/exception_information.h',
|
||||
'linux/scoped_ptrace_attach.cc',
|
||||
'linux/scoped_ptrace_attach.h',
|
||||
'linux/thread_info.cc',
|
||||
|
@ -38,8 +38,6 @@ class ExceptionHandlerServer {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
virtual ~Delegate();
|
||||
|
||||
//! \brief Called when the server has created the named pipe connection
|
||||
//! points and is ready to service requests.
|
||||
virtual void ExceptionHandlerServerStarted() = 0;
|
||||
@ -60,6 +58,9 @@ class ExceptionHandlerServer {
|
||||
HANDLE process,
|
||||
WinVMAddress exception_information_address,
|
||||
WinVMAddress debug_critical_section_address) = 0;
|
||||
|
||||
protected:
|
||||
~Delegate();
|
||||
};
|
||||
|
||||
//! \brief Constructs the exception handling server.
|
||||
|
@ -56,7 +56,7 @@ class RunServerThread : public Thread {
|
||||
class TestDelegate : public ExceptionHandlerServer::Delegate {
|
||||
public:
|
||||
explicit TestDelegate(HANDLE server_ready) : server_ready_(server_ready) {}
|
||||
~TestDelegate() override {}
|
||||
~TestDelegate() {}
|
||||
|
||||
void ExceptionHandlerServerStarted() override {
|
||||
SetEvent(server_ready_);
|
||||
|
Loading…
x
Reference in New Issue
Block a user