2020-02-18 14:49:10 -05:00
|
|
|
|
// Copyright 2020 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 "client/crashpad_client.h"
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
#include <ios>
|
|
|
|
|
|
2020-02-18 14:49:10 -05:00
|
|
|
|
#include "base/logging.h"
|
2020-04-25 17:16:10 -04:00
|
|
|
|
#include "base/mac/mach_logging.h"
|
|
|
|
|
#include "base/mac/scoped_mach_port.h"
|
|
|
|
|
#include "base/stl_util.h"
|
2020-03-04 15:19:46 -05:00
|
|
|
|
#include "snapshot/ios/process_snapshot_ios.h"
|
2020-04-08 15:37:55 -04:00
|
|
|
|
#include "util/ios/exception_processor.h"
|
2020-03-25 16:12:22 -04:00
|
|
|
|
#include "util/ios/ios_system_data_collector.h"
|
2020-04-25 17:16:10 -04:00
|
|
|
|
#include "util/mach/exc_server_variants.h"
|
|
|
|
|
#include "util/mach/exception_ports.h"
|
|
|
|
|
#include "util/mach/mach_extensions.h"
|
|
|
|
|
#include "util/mach/mach_message.h"
|
|
|
|
|
#include "util/mach/mach_message_server.h"
|
|
|
|
|
#include "util/misc/initialization_state_dcheck.h"
|
2020-02-18 14:49:10 -05:00
|
|
|
|
#include "util/posix/signals.h"
|
2020-04-25 17:16:10 -04:00
|
|
|
|
#include "util/thread/thread.h"
|
2020-02-18 14:49:10 -05:00
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
// A base class for signal handler and Mach exception server.
|
|
|
|
|
class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
|
2020-02-18 14:49:10 -05:00
|
|
|
|
public:
|
2020-04-25 17:16:10 -04:00
|
|
|
|
static CrashHandler* Get() {
|
|
|
|
|
static CrashHandler* instance = new CrashHandler();
|
2020-02-18 14:49:10 -05:00
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
void Initialize() {
|
|
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
|
InstallMachExceptionHandler();
|
|
|
|
|
CHECK(Signals::InstallHandler(SIGABRT, CatchSignal, 0, &old_action_));
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
2020-02-18 14:49:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 23:20:30 -05:00
|
|
|
|
void ProcessIntermediateDumps(
|
|
|
|
|
const std::map<std::string, std::string>& annotations = {}) {}
|
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
void DumpWithoutCrash(NativeCPUContext* context) {
|
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
mach_exception_data_type_t code[2] = {};
|
|
|
|
|
static constexpr int kSimulatedException = -1;
|
|
|
|
|
HandleMachException(MACH_EXCEPTION_CODES,
|
|
|
|
|
mach_thread_self(),
|
|
|
|
|
kSimulatedException,
|
|
|
|
|
code,
|
|
|
|
|
base::size(code),
|
|
|
|
|
MACHINE_THREAD_STATE,
|
|
|
|
|
reinterpret_cast<ConstThreadState>(context),
|
|
|
|
|
MACHINE_THREAD_STATE_COUNT);
|
2020-03-04 15:19:46 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 14:49:10 -05:00
|
|
|
|
private:
|
2020-04-25 17:16:10 -04:00
|
|
|
|
CrashHandler() = default;
|
|
|
|
|
|
|
|
|
|
void InstallMachExceptionHandler() {
|
|
|
|
|
exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
|
|
|
|
CHECK(exception_port_.is_valid());
|
|
|
|
|
|
|
|
|
|
kern_return_t kr = mach_port_insert_right(mach_task_self(),
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
MACH_MSG_TYPE_MAKE_SEND);
|
|
|
|
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
|
|
|
|
|
|
|
|
|
|
// TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.
|
|
|
|
|
const exception_mask_t mask =
|
|
|
|
|
ExcMaskAll() &
|
|
|
|
|
~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
|
|
|
|
|
EXC_MASK_RPC_ALERT | EXC_MASK_GUARD);
|
|
|
|
|
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
|
|
|
|
|
exception_ports.GetExceptionPorts(mask, &original_handlers_);
|
|
|
|
|
exception_ports.SetExceptionPort(
|
|
|
|
|
mask,
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
|
|
|
|
MACHINE_THREAD_STATE);
|
|
|
|
|
|
|
|
|
|
Start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Thread:
|
|
|
|
|
|
|
|
|
|
void ThreadMain() override {
|
|
|
|
|
UniversalMachExcServer universal_mach_exc_server(this);
|
|
|
|
|
while (true) {
|
|
|
|
|
mach_msg_return_t mr =
|
|
|
|
|
MachMessageServer::Run(&universal_mach_exc_server,
|
|
|
|
|
exception_port_.get(),
|
|
|
|
|
MACH_MSG_OPTION_NONE,
|
|
|
|
|
MachMessageServer::kPersistent,
|
|
|
|
|
MachMessageServer::kReceiveLargeIgnore,
|
|
|
|
|
kMachMessageTimeoutWaitIndefinitely);
|
|
|
|
|
MACH_CHECK(mr == MACH_SEND_INVALID_DEST, mr) << "MachMessageServer::Run";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UniversalMachExcServer::Interface:
|
|
|
|
|
|
|
|
|
|
kern_return_t CatchMachException(exception_behavior_t behavior,
|
|
|
|
|
exception_handler_t exception_port,
|
|
|
|
|
thread_t thread,
|
|
|
|
|
task_t task,
|
|
|
|
|
exception_type_t exception,
|
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
|
thread_state_flavor_t* flavor,
|
|
|
|
|
ConstThreadState old_state,
|
|
|
|
|
mach_msg_type_number_t old_state_count,
|
|
|
|
|
thread_state_t new_state,
|
|
|
|
|
mach_msg_type_number_t* new_state_count,
|
|
|
|
|
const mach_msg_trailer_t* trailer,
|
|
|
|
|
bool* destroy_complex_request) override {
|
|
|
|
|
*destroy_complex_request = true;
|
|
|
|
|
|
|
|
|
|
// TODO(justincohen): Forward exceptions to original_handlers_ with
|
|
|
|
|
// UniversalExceptionRaise.
|
|
|
|
|
|
|
|
|
|
// iOS shouldn't have any child processes, but just in case, those will
|
|
|
|
|
// inherit the task exception ports, and this process isn’t prepared to
|
|
|
|
|
// handle them
|
|
|
|
|
if (task != mach_task_self()) {
|
|
|
|
|
LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
|
|
|
|
|
<< mach_task_self();
|
|
|
|
|
return KERN_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HandleMachException(behavior,
|
|
|
|
|
thread,
|
|
|
|
|
exception,
|
|
|
|
|
code,
|
|
|
|
|
code_count,
|
|
|
|
|
*flavor,
|
|
|
|
|
old_state,
|
|
|
|
|
old_state_count);
|
|
|
|
|
|
|
|
|
|
// Respond with KERN_FAILURE so the system will continue to handle this
|
|
|
|
|
// exception as a crash.
|
|
|
|
|
return KERN_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HandleMachException(exception_behavior_t behavior,
|
|
|
|
|
thread_t thread,
|
|
|
|
|
exception_type_t exception,
|
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
|
thread_state_flavor_t flavor,
|
|
|
|
|
ConstThreadState old_state,
|
|
|
|
|
mach_msg_type_number_t old_state_count) {
|
|
|
|
|
// TODO(justincohen): This is incomplete.
|
|
|
|
|
ProcessSnapshotIOS process_snapshot;
|
|
|
|
|
process_snapshot.Initialize(system_data_);
|
|
|
|
|
process_snapshot.SetExceptionFromMachException(behavior,
|
|
|
|
|
thread,
|
|
|
|
|
exception,
|
|
|
|
|
code,
|
|
|
|
|
code_count,
|
|
|
|
|
flavor,
|
|
|
|
|
old_state,
|
|
|
|
|
old_state_count);
|
2020-02-18 14:49:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The signal handler installed at OS-level.
|
2020-04-25 17:16:10 -04:00
|
|
|
|
static void CatchSignal(int signo, siginfo_t* siginfo, void* context) {
|
|
|
|
|
Get()->HandleAndReraiseSignal(
|
|
|
|
|
signo, siginfo, reinterpret_cast<ucontext_t*>(context));
|
2020-02-18 14:49:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
void HandleAndReraiseSignal(int signo,
|
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
|
ucontext_t* context) {
|
|
|
|
|
// TODO(justincohen): This is incomplete.
|
|
|
|
|
ProcessSnapshotIOS process_snapshot;
|
|
|
|
|
process_snapshot.Initialize(system_data_);
|
|
|
|
|
process_snapshot.SetExceptionFromSignal(siginfo, context);
|
2020-02-18 14:49:10 -05:00
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
// Always call system handler.
|
|
|
|
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
|
|
|
|
|
}
|
2020-03-25 16:12:22 -04:00
|
|
|
|
|
2020-04-25 17:16:10 -04:00
|
|
|
|
base::mac::ScopedMachReceiveRight exception_port_;
|
|
|
|
|
ExceptionPorts::ExceptionHandlerVector original_handlers_;
|
|
|
|
|
struct sigaction old_action_ = {};
|
2021-01-26 10:06:27 -05:00
|
|
|
|
internal::IOSSystemDataCollector system_data_;
|
2020-04-25 17:16:10 -04:00
|
|
|
|
InitializationStateDcheck initialized_;
|
|
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CrashHandler);
|
2020-02-18 14:49:10 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
CrashpadClient::CrashpadClient() {}
|
|
|
|
|
|
|
|
|
|
CrashpadClient::~CrashpadClient() {}
|
|
|
|
|
|
2021-02-03 23:20:30 -05:00
|
|
|
|
// static
|
2021-02-02 09:02:28 -05:00
|
|
|
|
void CrashpadClient::StartCrashpadInProcessHandler(
|
|
|
|
|
const base::FilePath& database,
|
|
|
|
|
const std::string& url,
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
2020-04-08 15:37:55 -04:00
|
|
|
|
InstallObjcExceptionPreprocessor();
|
2020-04-25 17:16:10 -04:00
|
|
|
|
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->Initialize();
|
2020-02-18 14:49:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 23:20:30 -05:00
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::ProcessIntermediateDumps(
|
|
|
|
|
const std::map<std::string, std::string>& annotations) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->ProcessIntermediateDumps(annotations);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 22:38:31 -05:00
|
|
|
|
// static
|
2021-03-24 18:22:41 -04:00
|
|
|
|
void CrashpadClient::StartProcesingPendingReports() {
|
2021-02-16 22:38:31 -05:00
|
|
|
|
// TODO(justincohen): Start the CrashReportUploadThread.
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-04 15:19:46 -05:00
|
|
|
|
// static
|
2020-04-25 17:16:10 -04:00
|
|
|
|
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->DumpWithoutCrash(context);
|
2021-03-24 18:22:41 -04:00
|
|
|
|
// TODO(justincohen): Change this to only process the dump from above, not all
|
|
|
|
|
// intermediate dump files.
|
2021-02-03 23:20:30 -05:00
|
|
|
|
crash_handler->ProcessIntermediateDumps();
|
2020-03-04 15:19:46 -05:00
|
|
|
|
}
|
2020-04-08 15:37:55 -04:00
|
|
|
|
|
2021-03-24 18:22:41 -04:00
|
|
|
|
// static
|
|
|
|
|
void CrashpadClient::DumpWithoutCrashAndDeferProcessing(
|
|
|
|
|
NativeCPUContext* context) {
|
|
|
|
|
CrashHandler* crash_handler = CrashHandler::Get();
|
|
|
|
|
DCHECK(crash_handler);
|
|
|
|
|
crash_handler->DumpWithoutCrash(context);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 14:49:10 -05:00
|
|
|
|
} // namespace crashpad
|