Add exc_server_variants including UniversalMachExcServer and its test.

TEST=util_test ExcServerVariants.*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/545053003
This commit is contained in:
Mark Mentovai 2014-09-10 17:29:07 -04:00
parent 10d1b76b90
commit 177f5dcddc
8 changed files with 2244 additions and 14 deletions

View File

@ -25,6 +25,7 @@
#include "base/mac/scoped_mach_port.h"
#include "base/rand_util.h"
#include "gtest/gtest.h"
#include "util/mach/mach_extensions.h"
#include "util/test/mac/mach_errors.h"
namespace {
@ -51,21 +52,21 @@ TEST(Bootstrap, BootstrapCheckIn) {
kr = BootstrapCheckIn(bootstrap_port, service_name.c_str(), &server_port);
ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
<< BootstrapErrorMessage(kr, "bootstrap_check_in");
ASSERT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), server_port);
ASSERT_NE(kMachPortNull, server_port);
base::mac::ScopedMachReceiveRight server_port_owner(server_port);
// A subsequent checkin attempt should fail.
mach_port_t fail_port = MACH_PORT_NULL;
kr = BootstrapCheckIn(bootstrap_port, service_name.c_str(), &fail_port);
EXPECT_EQ(BOOTSTRAP_SERVICE_ACTIVE, kr);
EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), fail_port);
EXPECT_EQ(kMachPortNull, fail_port);
// Look up the service, getting a send right.
kr = bootstrap_look_up(bootstrap_port, service_name.c_str(), &client_port);
ASSERT_EQ(BOOTSTRAP_SUCCESS, kr)
<< BootstrapErrorMessage(kr, "bootstrap_look_up");
base::mac::ScopedMachSendRight client_port_owner(client_port);
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), client_port);
EXPECT_NE(kMachPortNull, client_port);
// Have the “client” send a message to the “server”.
struct SendMessage {

View File

@ -0,0 +1,674 @@
// Copyright 2014 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/mach/exc_server_variants.h"
#include <algorithm>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#include "util/mach/exc.h"
#include "util/mach/excServer.h"
#include "util/mach/mach_exc.h"
#include "util/mach/mach_excServer.h"
extern "C" {
// These six functions are not used, and are in fact obsoleted by the other
// functionality implemented in this file. The standard MIG-generated exc_server
// (in excServer.c) and mach_exc_server (in mach_excServer.c) server dispatch
// routines usable with the standard mach_msg_server() function call out to
// these functions. exc_server() and mach_exc_server() are unused and are
// replaced by the more flexible ExcServer and MachExcServer, but the linker
// still needs to see these six function definitions.
kern_return_t catch_exception_raise(exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
NOTREACHED();
return KERN_FAILURE;
}
kern_return_t catch_exception_raise_state(
exception_handler_t exception_port,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
thread_state_t old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
NOTREACHED();
return KERN_FAILURE;
}
kern_return_t catch_exception_raise_state_identity(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
thread_state_t old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
NOTREACHED();
return KERN_FAILURE;
}
kern_return_t catch_mach_exception_raise(exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t code_count) {
NOTREACHED();
return KERN_FAILURE;
}
kern_return_t catch_mach_exception_raise_state(
exception_handler_t exception_port,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
thread_state_t old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
NOTREACHED();
return KERN_FAILURE;
}
kern_return_t catch_mach_exception_raise_state_identity(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
thread_state_t old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
NOTREACHED();
return KERN_FAILURE;
}
} // extern "C"
namespace {
void PrepareReplyFromRequest(const mach_msg_header_t* in_header,
mach_msg_header_t* out_header) {
out_header->msgh_bits =
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0);
out_header->msgh_remote_port = in_header->msgh_remote_port;
out_header->msgh_size = sizeof(mig_reply_error_t);
out_header->msgh_local_port = MACH_PORT_NULL;
out_header->msgh_id = in_header->msgh_id + 100;
reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record;
}
void SetReplyError(mach_msg_header_t* out_header, kern_return_t error) {
reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error;
}
// There are no predefined constants for these.
enum MachMessageID : mach_msg_id_t {
kMachMessageIDExceptionRaise = 2401,
kMachMessageIDExceptionRaiseState = 2402,
kMachMessageIDExceptionRaiseStateIdentity = 2403,
kMachMessageIDMachExceptionRaise = 2405,
kMachMessageIDMachExceptionRaiseState = 2406,
kMachMessageIDMachExceptionRaiseStateIdentity = 2407,
};
} // namespace
namespace crashpad {
namespace internal {
ExcServer::ExcServer(ExcServer::Interface* interface)
: MachMessageServer::Interface(),
interface_(interface) {
}
bool ExcServer::MachMessageServerFunction(mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) {
PrepareReplyFromRequest(in_header, out_header);
switch (in_header->msgh_id) {
case kMachMessageIDExceptionRaise: {
// exception_raise(), catch_exception_raise().
typedef __Request__exception_raise_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
kern_return_t kr = __MIG_check__Request__exception_raise_t(in_request);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__exception_raise_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->RetCode =
interface_->CatchExceptionRaise(in_header->msgh_local_port,
in_request->thread.name,
in_request->task.name,
in_request->exception,
in_request->code,
in_request->codeCnt,
destroy_complex_request);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_header->msgh_size = sizeof(*out_reply);
return true;
}
case kMachMessageIDExceptionRaiseState: {
// exception_raise_state(), catch_exception_raise_state().
typedef __Request__exception_raise_state_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
// in_request_1 is used for the portion of the request after the codes,
// which in theory can be variable-length. The check function will set it.
Request* in_request_1;
kern_return_t kr = __MIG_check__Request__exception_raise_state_t(
in_request, &in_request_1);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__exception_raise_state_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->new_stateCnt = arraysize(out_reply->new_state);
out_reply->RetCode =
interface_->CatchExceptionRaiseState(in_header->msgh_local_port,
in_request->exception,
in_request->code,
in_request->codeCnt,
&in_request_1->flavor,
in_request_1->old_state,
in_request_1->old_stateCnt,
out_reply->new_state,
&out_reply->new_stateCnt);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_reply->flavor = in_request_1->flavor;
out_header->msgh_size =
sizeof(*out_reply) - sizeof(out_reply->new_state) +
sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
return true;
}
case kMachMessageIDExceptionRaiseStateIdentity: {
// exception_raise_state_identity(),
// catch_exception_raise_state_identity().
typedef __Request__exception_raise_state_identity_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
// in_request_1 is used for the portion of the request after the codes,
// which in theory can be variable-length. The check function will set it.
Request* in_request_1;
kern_return_t kr = __MIG_check__Request__exception_raise_state_identity_t(
in_request, &in_request_1);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__exception_raise_state_identity_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->new_stateCnt = arraysize(out_reply->new_state);
out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(
in_header->msgh_local_port,
in_request->thread.name,
in_request->task.name,
in_request->exception,
in_request->code,
in_request->codeCnt,
&in_request_1->flavor,
in_request_1->old_state,
in_request_1->old_stateCnt,
out_reply->new_state,
&out_reply->new_stateCnt,
destroy_complex_request);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_reply->flavor = in_request_1->flavor;
out_header->msgh_size =
sizeof(*out_reply) - sizeof(out_reply->new_state) +
sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
return true;
}
}
SetReplyError(out_header, MIG_BAD_ID);
return false;
}
mach_msg_size_t ExcServer::MachMessageServerRequestSize() {
return sizeof(__RequestUnion__exc_subsystem);
}
mach_msg_size_t ExcServer::MachMessageServerReplySize() {
return sizeof(__ReplyUnion__exc_subsystem);
}
MachExcServer::MachExcServer(MachExcServer::Interface* interface)
: MachMessageServer::Interface(),
interface_(interface) {
}
bool MachExcServer::MachMessageServerFunction(mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) {
PrepareReplyFromRequest(in_header, out_header);
switch (in_header->msgh_id) {
case kMachMessageIDMachExceptionRaise: {
// mach_exception_raise(), catch_mach_exception_raise().
typedef __Request__mach_exception_raise_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
kern_return_t kr =
__MIG_check__Request__mach_exception_raise_t(in_request);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__mach_exception_raise_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->RetCode =
interface_->CatchMachExceptionRaise(in_header->msgh_local_port,
in_request->thread.name,
in_request->task.name,
in_request->exception,
in_request->code,
in_request->codeCnt,
destroy_complex_request);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_header->msgh_size = sizeof(*out_reply);
return true;
}
case kMachMessageIDMachExceptionRaiseState: {
// mach_exception_raise_state(), catch_mach_exception_raise_state().
typedef __Request__mach_exception_raise_state_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
// in_request_1 is used for the portion of the request after the codes,
// which in theory can be variable-length. The check function will set it.
Request* in_request_1;
kern_return_t kr = __MIG_check__Request__mach_exception_raise_state_t(
in_request, &in_request_1);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__mach_exception_raise_state_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->new_stateCnt = arraysize(out_reply->new_state);
out_reply->RetCode =
interface_->CatchMachExceptionRaiseState(in_header->msgh_local_port,
in_request->exception,
in_request->code,
in_request->codeCnt,
&in_request_1->flavor,
in_request_1->old_state,
in_request_1->old_stateCnt,
out_reply->new_state,
&out_reply->new_stateCnt);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_reply->flavor = in_request_1->flavor;
out_header->msgh_size =
sizeof(*out_reply) - sizeof(out_reply->new_state) +
sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
return true;
}
case kMachMessageIDMachExceptionRaiseStateIdentity: {
// mach_exception_raise_state_identity(),
// catch_mach_exception_raise_state_identity().
typedef __Request__mach_exception_raise_state_identity_t Request;
Request* in_request = reinterpret_cast<Request*>(in_header);
// in_request_1 is used for the portion of the request after the codes,
// which in theory can be variable-length. The check function will set it.
Request* in_request_1;
kern_return_t kr =
__MIG_check__Request__mach_exception_raise_state_identity_t(
in_request, &in_request_1);
if (kr != MACH_MSG_SUCCESS) {
SetReplyError(out_header, kr);
return true;
}
typedef __Reply__mach_exception_raise_state_identity_t Reply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->new_stateCnt = arraysize(out_reply->new_state);
out_reply->RetCode = interface_->CatchMachExceptionRaiseStateIdentity(
in_header->msgh_local_port,
in_request->thread.name,
in_request->task.name,
in_request->exception,
in_request->code,
in_request->codeCnt,
&in_request_1->flavor,
in_request_1->old_state,
in_request_1->old_stateCnt,
out_reply->new_state,
&out_reply->new_stateCnt,
destroy_complex_request);
if (out_reply->RetCode != KERN_SUCCESS) {
return true;
}
out_reply->flavor = in_request_1->flavor;
out_header->msgh_size =
sizeof(*out_reply) - sizeof(out_reply->new_state) +
sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;
return true;
}
}
SetReplyError(out_header, MIG_BAD_ID);
return false;
}
mach_msg_size_t MachExcServer::MachMessageServerRequestSize() {
return sizeof(__RequestUnion__mach_exc_subsystem);
}
mach_msg_size_t MachExcServer::MachMessageServerReplySize() {
return sizeof(__ReplyUnion__mach_exc_subsystem);
}
SimplifiedExcServer::SimplifiedExcServer(
SimplifiedExcServer::Interface* interface)
: ExcServer(this),
ExcServer::Interface(),
interface_(interface) {
}
kern_return_t SimplifiedExcServer::CatchExceptionRaise(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
bool* destroy_request) {
thread_state_flavor_t flavor = THREAD_STATE_NONE;
mach_msg_type_number_t new_state_count = 0;
return interface_->CatchException(EXCEPTION_DEFAULT,
exception_port,
thread,
task,
exception,
code,
code_count,
&flavor,
NULL,
0,
NULL,
&new_state_count,
destroy_request);
}
kern_return_t SimplifiedExcServer::CatchExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
bool destroy_complex_request = false;
return interface_->CatchException(EXCEPTION_STATE,
exception_port,
MACH_PORT_NULL,
MACH_PORT_NULL,
exception,
code,
code_count,
flavor,
old_state,
old_state_count,
new_state,
new_state_count,
&destroy_complex_request);
}
kern_return_t SimplifiedExcServer::CatchExceptionRaiseStateIdentity(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) {
return interface_->CatchException(EXCEPTION_STATE_IDENTITY,
exception_port,
thread,
task,
exception,
code,
code_count,
flavor,
old_state,
old_state_count,
new_state,
new_state_count,
destroy_request);
}
SimplifiedMachExcServer::SimplifiedMachExcServer(
SimplifiedMachExcServer::Interface* interface)
: MachExcServer(this),
MachExcServer::Interface(),
interface_(interface) {
}
kern_return_t SimplifiedMachExcServer::CatchMachExceptionRaise(
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,
bool* destroy_request) {
thread_state_flavor_t flavor = THREAD_STATE_NONE;
mach_msg_type_number_t new_state_count = 0;
return interface_->CatchMachException(
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
exception_port,
thread,
task,
exception,
code,
code_count,
&flavor,
NULL,
0,
NULL,
&new_state_count,
destroy_request);
}
kern_return_t SimplifiedMachExcServer::CatchMachExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
bool destroy_complex_request = false;
return interface_->CatchMachException(EXCEPTION_STATE | MACH_EXCEPTION_CODES,
exception_port,
MACH_PORT_NULL,
MACH_PORT_NULL,
exception,
code,
code_count,
flavor,
old_state,
old_state_count,
new_state,
new_state_count,
&destroy_complex_request);
}
kern_return_t SimplifiedMachExcServer::CatchMachExceptionRaiseStateIdentity(
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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) {
return interface_->CatchMachException(
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
exception_port,
thread,
task,
exception,
code,
code_count,
flavor,
old_state,
old_state_count,
new_state,
new_state_count,
destroy_request);
}
} // namespace internal
UniversalMachExcServer::UniversalMachExcServer()
: MachMessageServer::Interface(),
internal::SimplifiedExcServer::Interface(),
internal::SimplifiedMachExcServer::Interface(),
exc_server_(this),
mach_exc_server_(this) {
}
bool UniversalMachExcServer::MachMessageServerFunction(
mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) {
switch (in_header->msgh_id) {
case kMachMessageIDMachExceptionRaise:
case kMachMessageIDMachExceptionRaiseState:
case kMachMessageIDMachExceptionRaiseStateIdentity:
return mach_exc_server_.MachMessageServerFunction(
in_header, out_header, destroy_complex_request);
case kMachMessageIDExceptionRaise:
case kMachMessageIDExceptionRaiseState:
case kMachMessageIDExceptionRaiseStateIdentity:
return exc_server_.MachMessageServerFunction(
in_header, out_header, destroy_complex_request);
}
// Do what the MIG-generated server routines do when they cant dispatch a
// message.
PrepareReplyFromRequest(in_header, out_header);
SetReplyError(out_header, MIG_BAD_ID);
return false;
}
mach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() {
return std::max(mach_exc_server_.MachMessageServerRequestSize(),
exc_server_.MachMessageServerRequestSize());
}
mach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() {
return std::max(mach_exc_server_.MachMessageServerReplySize(),
exc_server_.MachMessageServerReplySize());
}
kern_return_t UniversalMachExcServer::CatchException(
exception_behavior_t behavior,
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) {
std::vector<mach_exception_data_type_t> mach_codes;
mach_codes.reserve(code_count);
for (size_t index = 0; index < code_count; ++index) {
mach_codes.push_back(code[index]);
}
return CatchMachException(behavior,
exception_port,
thread,
task,
exception,
code_count ? &mach_codes[0] : NULL,
code_count,
flavor,
old_state,
old_state_count,
new_state,
new_state_count,
destroy_complex_request);
}
} // namespace crashpad

View File

@ -0,0 +1,436 @@
// Copyright 2014 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_MACH_EXC_SERVER_VARIANTS_H_
#define CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_
#include <mach/mach.h>
#include "util/mach/mach_message_server.h"
namespace crashpad {
// Routines to provide a single unified front-end to the interfaces in
// <mach/exc.defs> and <mach/mach_exc.defs>. The two interfaces are identical,
// except that the latter allows for 64-bit exception codes, and is requested by
// setting the MACH_EXCEPTION_CODES behavior bit associated with an exception
// port.
namespace internal {
//! \brief A server interface for the `exc` Mach subsystem.
class ExcServer : public MachMessageServer::Interface {
public:
//! \brief An interface that the different request messages that are a part of
//! the `exc` Mach subsystem can be dispatched to.
class Interface {
public:
//! \brief Handles exceptions raised by `exception_raise()`.
//!
//! This behaves equivalently to a `catch_exception_raise()` function used
//! with `exc_server()`.
//!
//! \param[out] destroy_request `true` if the request message is to be
//! destroyed even when this method returns success. See
//! MachMessageServer::Interface.
virtual kern_return_t CatchExceptionRaise(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
bool* destroy_request) = 0;
//! \brief Handles exceptions raised by `exception_raise_state()`.
//!
//! This behaves equivalently to a `catch_exception_raise_state()` function
//! used with `exc_server()`.
//!
//! There is no \a destroy_request parameter because, unlike
//! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the
//! request message is not complex (it does not carry the \a thread or \a
//! task port rights) and thus there is nothing to destroy.
virtual kern_return_t CatchExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) = 0;
//! \brief Handles exceptions raised by `exception_raise_state_identity()`.
//!
//! This behaves equivalently to a `catch_exception_raise_state_identity()`
//! function used with `exc_server()`.
//!
//! \param[out] destroy_request `true` if the request message is to be
//! destroyed even when this method returns success. See
//! MachMessageServer::Interface.
virtual kern_return_t CatchExceptionRaiseStateIdentity(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) = 0;
protected:
~Interface() {}
};
explicit ExcServer(Interface* interface);
// MachMessageServer::Interface:
virtual bool MachMessageServerFunction(
mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) override;
virtual mach_msg_size_t MachMessageServerRequestSize() override;
virtual mach_msg_size_t MachMessageServerReplySize() override;
private:
Interface* interface_; // weak
};
//! \brief A server interface for the `mach_exc` Mach subsystem.
class MachExcServer : public MachMessageServer::Interface {
public:
//! \brief An interface that the different request messages that are a part of
//! the `mach_exc` Mach subsystem can be dispatched to.
class Interface {
public:
//! \brief Handles exceptions raised by `mach_exception_raise()`.
//!
//! This behaves equivalently to a `catch_mach_exception_raise()` function
//! used with `mach_exc_server()`.
//!
//! \param[out] destroy_request `true` if the request message is to be
//! destroyed even when this method returns success. See
//! MachMessageServer::Interface.
virtual kern_return_t CatchMachExceptionRaise(
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,
bool* destroy_request) = 0;
//! \brief Handles exceptions raised by `mach_exception_raise_state()`.
//!
//! This behaves equivalently to a `catch_mach_exception_raise_state()`
//! function used with `mach_exc_server()`.
//!
//! There is no \a destroy_request parameter because, unlike
//! CatchMachExceptionRaise() and CatchMachExceptionRaiseStateIdentity(),
//! the request message is not complex (it does not carry the \a thread or
//! \a task port rights) and thus there is nothing to destroy.
virtual kern_return_t CatchMachExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) = 0;
//! \brief Handles exceptions raised by
//! `mach_exception_raise_state_identity()`.
//!
//! This behaves equivalently to a
//! `catch_mach_exception_raise_state_identity()` function used with
//! `mach_exc_server()`.
//!
//! \param[out] destroy_request `true` if the request message is to be
//! destroyed even when this method returns success. See
//! MachMessageServer::Interface.
virtual kern_return_t CatchMachExceptionRaiseStateIdentity(
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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) = 0;
protected:
~Interface() {}
};
explicit MachExcServer(Interface* interface);
// MachMessageServer::Interface:
virtual bool MachMessageServerFunction(
mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) override;
virtual mach_msg_size_t MachMessageServerRequestSize() override;
virtual mach_msg_size_t MachMessageServerReplySize() override;
private:
Interface* interface_; // weak
};
//! \brief A server interface for the `exc` Mach subsystem, simplified to have
//! only a single interface method needing implementation.
class SimplifiedExcServer : public ExcServer, public ExcServer::Interface {
public:
//! \brief An interface that the different request messages that are a part of
//! the `exc` Mach subsystem can be dispatched to.
class Interface {
public:
//! \brief Handles exceptions raised by `exception_raise()`,
//! `exception_raise_state()`, and `exception_raise_state_identity()`.
//!
//! For convenience in implementation, these different “behaviors” of
//! exception messages are all mapped to a single interface method. The
//! exceptions original “behavior” is specified in the \a behavior
//! parameter. Only parameters that were supplied in the request message
//! are populated, other parameters are set to reasonable default values.
//!
//! The meanings of most parameters are identical to that of
//! ExcServer::Interface::CatchExceptionRaiseStateIdentity().
//!
//! \param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
//! `EXCEPTION_STATE_IDENTITY`, identifying which exception request
//! message was processed and thus which other parameters are valid.
virtual kern_return_t CatchException(
exception_behavior_t behavior,
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) = 0;
protected:
~Interface() {}
};
explicit SimplifiedExcServer(Interface* interface);
// ExcServer::Interface:
virtual kern_return_t CatchExceptionRaise(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
bool* destroy_request) override;
virtual kern_return_t CatchExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) override;
virtual kern_return_t CatchExceptionRaiseStateIdentity(
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) override;
private:
Interface* interface_; // weak
};
//! \brief A server interface for the `mach_exc` Mach subsystem, simplified to
//! have only a single interface method needing implementation.
class SimplifiedMachExcServer : public MachExcServer,
public MachExcServer::Interface {
public:
//! \brief An interface that the different request messages that are a part of
//! the `mach_exc` Mach subsystem can be dispatched to.
class Interface {
public:
//! \brief Handles exceptions raised by `mach_exception_raise()`,
//! `mach_exception_raise_state()`, and
//! `mach_exception_raise_state_identity()`.
//!
//! When used with UniversalMachExcServer, this also handles exceptions
//! raised by `exception_raise()`, `exception_raise_state()`, and
//! `exception_raise_state_identity()`.
//!
//! For convenience in implementation, these different “behaviors” of
//! exception messages are all mapped to a single interface method. The
//! exceptions original “behavior” is specified in the \a behavior
//! parameter. Only parameters that were supplied in the request message
//! are populated, other parameters are set to reasonable default values.
//!
//! The meanings of most parameters are identical to that of
//! MachExcServer::Interface::CatchMachExceptionRaiseStateIdentity().
//!
//! \param[in] behavior `MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT`,
//! `MACH_EXCEPTION_CODES | EXCEPTION_STATE`, or
//! `MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY`, identifying which
//! exception request message was processed and thus which other
//! parameters are valid. When used with UniversalMachExcServer, \a
//! behavior can also be `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or
//! `EXCEPTION_STATE_IDENTITY`.
virtual 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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) = 0;
protected:
~Interface() {}
};
explicit SimplifiedMachExcServer(Interface* interface);
// MachExcServer::Interface:
virtual kern_return_t CatchMachExceptionRaise(
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,
bool* destroy_request) override;
virtual kern_return_t CatchMachExceptionRaiseState(
exception_handler_t exception_port,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) override;
virtual kern_return_t CatchMachExceptionRaiseStateIdentity(
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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_request) override;
private:
Interface* interface_; // weak
};
} // namespace internal
//! \brief A server interface for the `exc` and `mach_exc` Mach subsystems,
//! unified to handle exceptions delivered to either subsystem, and
//! simplified to have only a single interface method needing
//! implementation.
//!
//! UniversalMachExcServer operates by translating messages received in the
//! `exc` subsystem to a variant that is compatible with the `mach_exc`
//! subsystem. This involves changing the format of \a code, the exception code
//! field, from `exception_data_type_t` to `mach_exception_data_type_t`.
//! This is achieved by implementing SimplifiedExcServer::Interface and having
//! it forward translated messages to SimplifiedMachExcServer::Interface, which
//! is left unimplemented here so that users of this class can provide their own
//! implementations.
class UniversalMachExcServer
: public MachMessageServer::Interface,
public internal::SimplifiedExcServer::Interface,
public internal::SimplifiedMachExcServer::Interface {
public:
UniversalMachExcServer();
// MachMessageServer::Interface:
virtual bool MachMessageServerFunction(
mach_msg_header_t* in_header,
mach_msg_header_t* out_header,
bool* destroy_complex_request) override;
virtual mach_msg_size_t MachMessageServerRequestSize() override;
virtual mach_msg_size_t MachMessageServerReplySize() override;
// internal::SimplifiedExcServer::Interface:
virtual kern_return_t CatchException(
exception_behavior_t behavior,
exception_handler_t exception_port,
thread_t thread,
task_t task,
exception_type_t exception,
const exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t* flavor,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) override;
private:
internal::SimplifiedExcServer exc_server_;
internal::SimplifiedMachExcServer mach_exc_server_;
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
// Copyright 2014 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_MACH_MACH_EXTENSIONS_H_
#define CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
#include <mach/mach.h>
namespace crashpad {
//! \brief `MACH_PORT_NULL` with the correct type for a Mach port,
//! `mach_port_t`.
//!
//! For situations where implicit conversions between signed and unsigned types
//! are not performed, use kMachPortNull instead of an explicit `static_cast` of
//! `MACH_PORT_NULL` to `mach_port_t`. This is useful for logging and testing
//! assertions.
const mach_port_t kMachPortNull = MACH_PORT_NULL;
// Because exception_mask_t is an int and has one bit for each defined
// exception_type_t, its reasonable to assume that there cannot be any
// officially-defined exception_type_t values higher than 31.
// kMachExceptionSimulated uses a value well outside this range because it does
// not require a corresponding mask value. Simulated exceptions are delivered to
// the exception handler registered for EXC_CRASH exceptions using
// EXC_MASK_CRASH.
//! \brief An exception type to use for simulated exceptions.
const exception_type_t kMachExceptionSimulated = 'CPsx';
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_

View File

@ -21,6 +21,7 @@
#include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/mach/mach_extensions.h"
#include "util/test/errors.h"
#include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h"
@ -214,8 +215,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
EXPECT_EQ(kRequestMessageId, request->header.msgh_id);
if (options_.client_send_complex) {
EXPECT_EQ(1u, request->body.msgh_descriptor_count);
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
request->port_descriptor.name);
EXPECT_NE(kMachPortNull, request->port_descriptor.name);
parent_complex_message_port_ = request->port_descriptor.name;
EXPECT_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND),
request->port_descriptor.disposition);
@ -224,8 +224,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
request->port_descriptor.type);
} else {
EXPECT_EQ(0u, request->body.msgh_descriptor_count);
EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
request->port_descriptor.name);
EXPECT_EQ(kMachPortNull, request->port_descriptor.name);
EXPECT_EQ(0u, request->port_descriptor.disposition);
EXPECT_EQ(0u, request->port_descriptor.type);
}
@ -318,8 +317,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
<< MachErrorMessage(kr, "MachMessageServer");
if (options_.client_send_complex) {
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
parent_complex_message_port_);
EXPECT_NE(kMachPortNull, parent_complex_message_port_);
mach_port_type_t type;
if (!options_.expect_server_destroyed_complex) {
@ -505,8 +503,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
ASSERT_EQ(static_cast<mach_msg_bits_t>(
MACH_MSGH_BITS(0, MACH_MSG_TYPE_MOVE_SEND)), reply.Head.msgh_bits);
ASSERT_EQ(sizeof(ReplyMessage), reply.Head.msgh_size);
ASSERT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
reply.Head.msgh_remote_port);
ASSERT_EQ(kMachPortNull, reply.Head.msgh_remote_port);
ASSERT_EQ(LocalPort(), reply.Head.msgh_local_port);
ASSERT_EQ(kReplyMessageId, reply.Head.msgh_id);
ASSERT_EQ(0, memcmp(&reply.NDR, &NDR_record, sizeof(NDR_record)));

View File

@ -27,6 +27,7 @@
#include "base/rand_util.h"
#include "gtest/gtest.h"
#include "util/mach/bootstrap.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/scoped_forbid_return.h"
#include "util/test/errors.h"
#include "util/test/mac/mach_errors.h"
@ -105,17 +106,17 @@ void MachMultiprocess::PreFork() {
}
mach_port_t MachMultiprocess::LocalPort() const {
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->local_port);
EXPECT_NE(kMachPortNull, info_->local_port);
return info_->local_port;
}
mach_port_t MachMultiprocess::RemotePort() const {
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->remote_port);
EXPECT_NE(kMachPortNull, info_->remote_port);
return info_->remote_port;
}
mach_port_t MachMultiprocess::ChildTask() const {
EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), info_->child_task);
EXPECT_NE(kMachPortNull, info_->child_task);
return info_->child_task;
}

View File

@ -61,6 +61,9 @@
'mac/process_types/traits.h',
'mach/bootstrap.cc',
'mach/bootstrap.h',
'mach/exc_server_variants.cc',
'mach/exc_server_variants.h',
'mach/mach_extensions.h',
'mach/mach_message_server.cc',
'mach/mach_message_server.h',
'mach/task_memory.cc',
@ -167,6 +170,7 @@
'util_test_lib',
'util_test_multiprocess_exec_test_child',
'../compat/compat.gyp:compat',
'../third_party/gmock/gmock.gyp:gmock',
'../third_party/gtest/gtest.gyp:gtest',
'../third_party/gtest/gtest.gyp:gtest_main',
'../third_party/mini_chromium/mini_chromium/base/base.gyp:base',
@ -185,6 +189,7 @@
'mac/process_types_test.cc',
'mac/service_management_test.mm',
'mach/bootstrap_test.cc',
'mach/exc_server_variants_test.cc',
'mach/mach_message_server_test.cc',
'mach/task_memory_test.cc',
'misc/initialization_state_dcheck_test.cc',