From c83e773c331c0555d62bae855fc438244586dcba Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 4 Dec 2014 16:45:02 -0500 Subject: [PATCH] Add CompositeMachMessageServer and its test. TEST=util_test CompositeMachMessageServer* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/781823002 --- util/mach/child_port_server.cc | 6 + util/mach/child_port_server.h | 2 + util/mach/child_port_server_test.cc | 4 + util/mach/composite_mach_message_server.cc | 86 +++++ util/mach/composite_mach_message_server.h | 103 ++++++ .../composite_mach_message_server_test.cc | 303 ++++++++++++++++++ util/mach/exc_server_variants.cc | 31 ++ util/mach/exc_server_variants.h | 8 + util/mach/exc_server_variants_test.cc | 46 +++ util/mach/mach_message_server.h | 6 + util/mach/mach_message_server_test.cc | 24 +- util/util.gyp | 3 + 12 files changed, 614 insertions(+), 8 deletions(-) create mode 100644 util/mach/composite_mach_message_server.cc create mode 100644 util/mach/composite_mach_message_server.h create mode 100644 util/mach/composite_mach_message_server_test.cc diff --git a/util/mach/child_port_server.cc b/util/mach/child_port_server.cc index 5f4b3224..500cd1e5 100644 --- a/util/mach/child_port_server.cc +++ b/util/mach/child_port_server.cc @@ -103,6 +103,12 @@ bool ChildPortServer::MachMessageServerFunction( return false; } +std::set ChildPortServer::MachMessageServerRequestIDs() { + const mach_msg_id_t request_ids[] = {kMachMessageIDChildPortCheckIn}; + return std::set( + &request_ids[0], &request_ids[arraysize(request_ids)]); +} + mach_msg_size_t ChildPortServer::MachMessageServerRequestSize() { return sizeof(__RequestUnion__handle_child_port_subsystem); } diff --git a/util/mach/child_port_server.h b/util/mach/child_port_server.h index 8083a1e8..fc83c150 100644 --- a/util/mach/child_port_server.h +++ b/util/mach/child_port_server.h @@ -62,6 +62,8 @@ class ChildPortServer : public MachMessageServer::Interface { mach_msg_header_t* out_header, bool* destroy_complex_request) override; + std::set MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; mach_msg_size_t MachMessageServerReplySize() override; diff --git a/util/mach/child_port_server_test.cc b/util/mach/child_port_server_test.cc index ddc525ba..b9ade377 100644 --- a/util/mach/child_port_server_test.cc +++ b/util/mach/child_port_server_test.cc @@ -101,6 +101,10 @@ TEST(ChildPortServer, MockChildPortCheckIn) { MockChildPortServerInterface server_interface; ChildPortServer server(&server_interface); + std::set expect_request_ids; + expect_request_ids.insert(10011); // There is no constant for this. + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + ChildPortCheckInRequest request; EXPECT_LE(request.Head.msgh_size, server.MachMessageServerRequestSize()); diff --git a/util/mach/composite_mach_message_server.cc b/util/mach/composite_mach_message_server.cc new file mode 100644 index 00000000..abbe5bcf --- /dev/null +++ b/util/mach/composite_mach_message_server.cc @@ -0,0 +1,86 @@ +// 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/composite_mach_message_server.h" + +#include +#include + +#include "base/logging.h" +#include "util/mach/mach_message.h" + +namespace crashpad { + +CompositeMachMessageServer::CompositeMachMessageServer() + : MachMessageServer::Interface(), + handler_map_(), + request_size_(sizeof(mach_msg_header_t)), + reply_size_(sizeof(mig_reply_error_t)) { +} + +CompositeMachMessageServer::~CompositeMachMessageServer() { +} + +void CompositeMachMessageServer::AddHandler( + MachMessageServer::Interface* handler) { + // Other cycles would be invalid as well, but they aren’t currently checked. + DCHECK_NE(handler, this); + + std::set request_ids = handler->MachMessageServerRequestIDs(); + for (mach_msg_id_t request_id : request_ids) { + std::pair result = + handler_map_.insert(std::make_pair(request_id, handler)); + CHECK(result.second) << "duplicate request ID " << request_id; + } + + request_size_ = + std::max(request_size_, handler->MachMessageServerRequestSize()); + reply_size_ = std::max(reply_size_, handler->MachMessageServerReplySize()); +} + +bool CompositeMachMessageServer::MachMessageServerFunction( + const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) { + HandlerMap::const_iterator iterator = handler_map_.find(in->msgh_id); + if (iterator == handler_map_.end()) { + // Do what MIG-generated server routines do when they can’t dispatch a + // message. + PrepareMIGReplyFromRequest(in, out); + SetMIGReplyError(out, MIG_BAD_ID); + return false; + } + + MachMessageServer::Interface* handler = iterator->second; + return handler->MachMessageServerFunction(in, out, destroy_complex_request); +} + +std::set +CompositeMachMessageServer::MachMessageServerRequestIDs() { + std::set request_ids; + for (const auto& entry : handler_map_) { + request_ids.insert(entry.first); + } + return request_ids; +} + +mach_msg_size_t CompositeMachMessageServer::MachMessageServerRequestSize() { + return request_size_; +} + +mach_msg_size_t CompositeMachMessageServer::MachMessageServerReplySize() { + return reply_size_; +} + +} // namespace crashpad diff --git a/util/mach/composite_mach_message_server.h b/util/mach/composite_mach_message_server.h new file mode 100644 index 00000000..6da957b7 --- /dev/null +++ b/util/mach/composite_mach_message_server.h @@ -0,0 +1,103 @@ +// 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_COMPOSITE_MACH_MESSAGE_SERVER_H_ +#define CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_ + +#include + +#include +#include + +#include "base/basictypes.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { + +//! \brief Adapts multiple MachMessageServer::Interface implementations for +//! simultaneous use in a single MachMessageServer::Run() call. +//! +//! This class implements a MachMessageServer::Interface that contains other +//! other MachMessageServer::Interface objects. +//! +//! In some situations, it may be desirable for a Mach message server to handle +//! messages from distinct MIG subsystems with distinct +//! MachMessageServer::Interface implementations. This may happen if a single +//! receive right is shared for multiple subsystems, or if distinct receive +//! rights are combined in a Mach port set. In these cases, this class performs +//! a first-level demultiplexing to forward request messages to the proper +//! subsystem-level demultiplexers. +class CompositeMachMessageServer : public MachMessageServer::Interface { + public: + CompositeMachMessageServer(); + ~CompositeMachMessageServer(); + + //! \brief Adds a handler that messages can be dispatched to based on request + //! message ID. + //! + //! \param[in] handler A MachMessageServer handler. Ownership of this object + //! is not taken. Cycles must not be created between objects. It is + //! invalid to add an object as its own handler. + //! + //! If \a handler claims to support any request ID that this object is already + //! able to handle, execution will be terminated. + void AddHandler(MachMessageServer::Interface* handler); + + // MachMessageServer::Interface: + + //! \copydoc MachMessageServer::Interface::MachMessageServerFunction() + //! + //! This implementation forwards the message to an appropriate handler added + //! by AddHandler() on the basis of the \a in request message’s message ID. + //! If no appropriate handler exists, the \a out reply message is treated as + //! a `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false` + //! is returned. + bool MachMessageServerFunction(const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerRequestIDs() + //! + //! This implementation returns the set of all request message Mach message + //! IDs of all handlers added by AddHandler(). + std::set MachMessageServerRequestIDs() override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerRequestSize() + //! + //! This implementation returns the maximum request message size of all + //! handlers added by AddHandler(). If no handlers are present, returns the + //! size of `mach_msg_header_t`, the minimum size of a MIG request message + //! that can be received for demultiplexing purposes. + mach_msg_size_t MachMessageServerRequestSize() override; + + //! \copydoc MachMessageServer::Interface::MachMessageServerReplySize() + //! + //! This implementation returns the maximum reply message size of all handlers + //! added by AddHandler(). If no handlers are present, returns the size of + //! `mig_reply_error_t`, the minimum size of a MIG reply message. + mach_msg_size_t MachMessageServerReplySize() override; + + private: + using HandlerMap = std::map; + + HandlerMap handler_map_; // weak + mach_msg_size_t request_size_; + mach_msg_size_t reply_size_; + + DISALLOW_COPY_AND_ASSIGN(CompositeMachMessageServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_ diff --git a/util/mach/composite_mach_message_server_test.cc b/util/mach/composite_mach_message_server_test.cc new file mode 100644 index 00000000..f1ec963c --- /dev/null +++ b/util/mach/composite_mach_message_server_test.cc @@ -0,0 +1,303 @@ +// 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/composite_mach_message_server.h" + +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/mach/mach_message.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(CompositeMachMessageServer, Empty) { + CompositeMachMessageServer server; + + EXPECT_TRUE(server.MachMessageServerRequestIDs().empty()); + + mach_msg_empty_rcv_t request = {}; + EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize()); + + mig_reply_error_t reply = {}; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); + + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); +} + +class TestMachMessageHandler : public MachMessageServer::Interface { + public: + TestMachMessageHandler() + : MachMessageServer::Interface(), + request_ids_(), + request_size_(0), + reply_size_(0), + return_code_(KERN_FAILURE), + return_value_(false), + destroy_complex_request_(false) { + } + + ~TestMachMessageHandler() { + } + + void SetReturnCodes(bool return_value, + kern_return_t return_code, + bool destroy_complex_request) { + return_value_ = return_value; + return_code_ = return_code; + destroy_complex_request_ = destroy_complex_request; + } + + void AddRequestID(mach_msg_id_t request_id) { + request_ids_.insert(request_id); + } + + void SetRequestSize(mach_msg_size_t request_size) { + request_size_ = request_size; + } + + void SetReplySize(mach_msg_size_t reply_size) { + reply_size_ = reply_size; + } + + // MachMessageServer::Interface: + + bool MachMessageServerFunction(const mach_msg_header_t* in, + mach_msg_header_t* out, + bool* destroy_complex_request) override { + EXPECT_NE(request_ids_.end(), request_ids_.find(in->msgh_id)); + + *destroy_complex_request = destroy_complex_request_; + PrepareMIGReplyFromRequest(in, out); + SetMIGReplyError(out, return_code_); + return return_value_; + } + + std::set MachMessageServerRequestIDs() override { + return request_ids_; + } + + mach_msg_size_t MachMessageServerRequestSize() override { + return request_size_; + } + + mach_msg_size_t MachMessageServerReplySize() override { + return reply_size_; + } + + private: + std::set request_ids_; + mach_msg_size_t request_size_; + mach_msg_size_t reply_size_; + kern_return_t return_code_; + bool return_value_; + bool destroy_complex_request_; + + DISALLOW_COPY_AND_ASSIGN(TestMachMessageHandler); +}; + +TEST(CompositeMachMessageServer, HandlerDoesNotHandle) { + TestMachMessageHandler handler; + + CompositeMachMessageServer server; + server.AddHandler(&handler); + + EXPECT_TRUE(server.MachMessageServerRequestIDs().empty()); + + mach_msg_empty_rcv_t request = {}; + EXPECT_EQ(sizeof(request.header), server.MachMessageServerRequestSize()); + + mig_reply_error_t reply = {}; + EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize()); + + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); +} + +TEST(CompositeMachMessageServer, OneHandler) { + const mach_msg_id_t kRequestID = 100; + const mach_msg_size_t kRequestSize = 256; + const mach_msg_size_t kReplySize = 128; + const kern_return_t kReturnCode = KERN_SUCCESS; + + TestMachMessageHandler handler; + handler.AddRequestID(kRequestID); + handler.SetRequestSize(kRequestSize); + handler.SetReplySize(kReplySize); + handler.SetReturnCodes(true, kReturnCode, true); + + CompositeMachMessageServer server; + + // The chosen request and reply sizes must be larger than the defaults for + // that portion fo the test to be valid. + EXPECT_GT(kRequestSize, server.MachMessageServerRequestSize()); + EXPECT_GT(kReplySize, server.MachMessageServerReplySize()); + + server.AddHandler(&handler); + + std::set expect_request_ids; + expect_request_ids.insert(kRequestID); + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + EXPECT_EQ(kRequestSize, server.MachMessageServerRequestSize()); + EXPECT_EQ(kReplySize, server.MachMessageServerReplySize()); + + mach_msg_empty_rcv_t request = {}; + mig_reply_error_t reply = {}; + + // Send a message with an unknown request ID. + request.header.msgh_id = 0; + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + + // Send a message with a known request ID. + request.header.msgh_id = kRequestID; + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); +} + +TEST(CompositeMachMessageServer, ThreeHandlers) { + const mach_msg_id_t kRequestIDs0[] = {5}; + const kern_return_t kReturnCode0 = KERN_SUCCESS; + + const mach_msg_id_t kRequestIDs1[] = {4, 7}; + const kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE; + + const mach_msg_id_t kRequestIDs2[] = {10, 0, 20}; + const mach_msg_size_t kRequestSize2 = 6144; + const mach_msg_size_t kReplySize2 = 16384; + const kern_return_t kReturnCode2 = KERN_NOT_RECEIVER; + + TestMachMessageHandler handlers[3]; + std::set expect_request_ids; + + for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) { + const mach_msg_id_t request_id = kRequestIDs0[index]; + handlers[0].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[0].SetRequestSize(sizeof(mach_msg_header_t)); + handlers[0].SetReplySize(sizeof(mig_reply_error_t)); + handlers[0].SetReturnCodes(true, kReturnCode0, false); + + for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) { + const mach_msg_id_t request_id = kRequestIDs1[index]; + handlers[1].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[1].SetRequestSize(100); + handlers[1].SetReplySize(200); + handlers[1].SetReturnCodes(false, kReturnCode1, true); + + for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) { + const mach_msg_id_t request_id = kRequestIDs2[index]; + handlers[2].AddRequestID(request_id); + expect_request_ids.insert(request_id); + } + handlers[2].SetRequestSize(kRequestSize2); + handlers[2].SetReplySize(kReplySize2); + handlers[2].SetReturnCodes(true, kReturnCode2, true); + + CompositeMachMessageServer server; + + // The chosen request and reply sizes must be larger than the defaults for + // that portion fo the test to be valid. + EXPECT_GT(kRequestSize2, server.MachMessageServerRequestSize()); + EXPECT_GT(kReplySize2, server.MachMessageServerReplySize()); + + server.AddHandler(&handlers[0]); + server.AddHandler(&handlers[1]); + server.AddHandler(&handlers[2]); + + EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs()); + + EXPECT_EQ(kRequestSize2, server.MachMessageServerRequestSize()); + EXPECT_EQ(kReplySize2, server.MachMessageServerReplySize()); + + mach_msg_empty_rcv_t request = {}; + mig_reply_error_t reply = {}; + + // Send a message with an unknown request ID. + request.header.msgh_id = 100; + bool destroy_complex_request = false; + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(MIG_BAD_ID, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + + // Send messages with known request IDs. + + for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) { + request.header.msgh_id = kRequestIDs0[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 0, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode0, reply.RetCode); + EXPECT_FALSE(destroy_complex_request); + } + + for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) { + request.header.msgh_id = kRequestIDs1[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 1, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_FALSE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode1, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); + } + + for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) { + request.header.msgh_id = kRequestIDs2[index]; + SCOPED_TRACE(base::StringPrintf( + "handler 2, index %zu, id %d", index, request.header.msgh_id)); + + EXPECT_TRUE(server.MachMessageServerFunction( + &request.header, &reply.Head, &destroy_complex_request)); + EXPECT_EQ(kReturnCode2, reply.RetCode); + EXPECT_TRUE(destroy_complex_request); + } +} + +// CompositeMachMessageServer can’t deal with two handlers that want to handle +// the same request ID. +TEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) { + const mach_msg_id_t kRequestID = 400; + + TestMachMessageHandler handlers[2]; + handlers[0].AddRequestID(kRequestID); + handlers[1].AddRequestID(kRequestID); + + CompositeMachMessageServer server; + + server.AddHandler(&handlers[0]); + EXPECT_DEATH(server.AddHandler(&handlers[1]), "duplicate request ID"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/mach/exc_server_variants.cc b/util/mach/exc_server_variants.cc index 0376e600..7dbe553f 100644 --- a/util/mach/exc_server_variants.cc +++ b/util/mach/exc_server_variants.cc @@ -318,6 +318,16 @@ bool ExcServer::MachMessageServerFunction(const mach_msg_header_t* in_header, return false; } +std::set ExcServer::MachMessageServerRequestIDs() { + const mach_msg_id_t request_ids[] = { + kMachMessageIDExceptionRaise, + kMachMessageIDExceptionRaiseState, + kMachMessageIDExceptionRaiseStateIdentity + }; + return std::set( + &request_ids[0], &request_ids[arraysize(request_ids)]); +} + mach_msg_size_t ExcServer::MachMessageServerRequestSize() { return sizeof(__RequestUnion__exc_subsystem); } @@ -459,6 +469,16 @@ bool MachExcServer::MachMessageServerFunction( return false; } +std::set MachExcServer::MachMessageServerRequestIDs() { + const mach_msg_id_t request_ids[] = { + kMachMessageIDMachExceptionRaise, + kMachMessageIDMachExceptionRaiseState, + kMachMessageIDMachExceptionRaiseStateIdentity + }; + return std::set( + &request_ids[0], &request_ids[arraysize(request_ids)]); +} + mach_msg_size_t MachExcServer::MachMessageServerRequestSize() { return sizeof(__RequestUnion__mach_exc_subsystem); } @@ -689,6 +709,17 @@ bool UniversalMachExcServer::MachMessageServerFunction( return false; } +std::set UniversalMachExcServer::MachMessageServerRequestIDs() { + std::set request_ids = + exc_server_.MachMessageServerRequestIDs(); + + std::set mach_exc_request_ids = + mach_exc_server_.MachMessageServerRequestIDs(); + request_ids.insert(mach_exc_request_ids.begin(), mach_exc_request_ids.end()); + + return request_ids; +} + mach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() { return std::max(mach_exc_server_.MachMessageServerRequestSize(), exc_server_.MachMessageServerRequestSize()); diff --git a/util/mach/exc_server_variants.h b/util/mach/exc_server_variants.h index 80fb659c..3552f99f 100644 --- a/util/mach/exc_server_variants.h +++ b/util/mach/exc_server_variants.h @@ -17,6 +17,8 @@ #include +#include + #include "build/build_config.h" #include "util/mach/mach_message_server.h" @@ -118,6 +120,8 @@ class ExcServer : public MachMessageServer::Interface { mach_msg_header_t* out_header, bool* destroy_complex_request) override; + std::set MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; mach_msg_size_t MachMessageServerReplySize() override; @@ -217,6 +221,8 @@ class MachExcServer : public MachMessageServer::Interface { mach_msg_header_t* out_header, bool* destroy_complex_request) override; + std::set MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; mach_msg_size_t MachMessageServerReplySize() override; @@ -483,6 +489,8 @@ class UniversalMachExcServer mach_msg_header_t* out_header, bool* destroy_complex_request) override; + std::set MachMessageServerRequestIDs() override; + mach_msg_size_t MachMessageServerRequestSize() override; mach_msg_size_t MachMessageServerReplySize() override; diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc index 84a65e21..e47d9f92 100644 --- a/util/mach/exc_server_variants_test.cc +++ b/util/mach/exc_server_variants_test.cc @@ -584,6 +584,10 @@ TEST(ExcServerVariants, MockExceptionRaise) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2401)); // There is no constant for this. + ExceptionRaiseRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -625,6 +629,10 @@ TEST(ExcServerVariants, MockExceptionRaiseState) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2402)); // There is no constant for this. + ExceptionRaiseStateRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -670,6 +678,10 @@ TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2403)); // There is no constant for this. + ExceptionRaiseStateIdentityRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -712,6 +724,10 @@ TEST(ExcServerVariants, MockMachExceptionRaise) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2405)); // There is no constant for this. + MachExceptionRaiseRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -755,6 +771,10 @@ TEST(ExcServerVariants, MockMachExceptionRaiseState) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2406)); // There is no constant for this. + MachExceptionRaiseStateRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -801,6 +821,10 @@ TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) { MockUniversalMachExcServer server; UniversalMachExcServer universal_mach_exc_server(&server); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_NE(ids.end(), ids.find(2407)); // There is no constant for this. + MachExceptionRaiseStateIdentityRequest request; EXPECT_LE(request.Head.msgh_size, universal_mach_exc_server.MachMessageServerRequestSize()); @@ -882,6 +906,10 @@ TEST(ExcServerVariants, MockUnknownID) { SCOPED_TRACE(base::StringPrintf("unknown id %d", id)); + std::set ids = + universal_mach_exc_server.MachMessageServerRequestIDs(); + EXPECT_EQ(ids.end(), ids.find(id)); + InvalidRequest request(id); EXPECT_LE(sizeof(request), universal_mach_exc_server.MachMessageServerRequestSize()); @@ -906,6 +934,24 @@ TEST(ExcServerVariants, MockUnknownID) { } } +TEST(ExcServerVariants, MachMessageServerRequestIDs) { + std::set expect_request_ids; + + // There are no constants for these. + expect_request_ids.insert(2401); + expect_request_ids.insert(2402); + expect_request_ids.insert(2403); + expect_request_ids.insert(2405); + expect_request_ids.insert(2406); + expect_request_ids.insert(2407); + + MockUniversalMachExcServer server; + UniversalMachExcServer universal_mach_exc_server(&server); + + EXPECT_EQ(expect_request_ids, + universal_mach_exc_server.MachMessageServerRequestIDs()); +} + class TestExcServerVariants : public MachMultiprocess, public UniversalMachExcServer::Interface { public: diff --git a/util/mach/mach_message_server.h b/util/mach/mach_message_server.h index fb77b30c..200892b2 100644 --- a/util/mach/mach_message_server.h +++ b/util/mach/mach_message_server.h @@ -17,6 +17,8 @@ #include +#include + #include "base/basictypes.h" namespace crashpad { @@ -68,6 +70,10 @@ class MachMessageServer { mach_msg_header_t* out, bool* destroy_complex_request) = 0; + //! \return The set of request message Mach message IDs that + //! MachMessageServerFunction() is able to handle. + virtual std::set MachMessageServerRequestIDs() = 0; + //! \return The expected or maximum size, in bytes, of a request message to //! be received as the \a in parameter of MachMessageServerFunction(). virtual mach_msg_size_t MachMessageServerRequestSize() = 0; diff --git a/util/mach/mach_message_server_test.cc b/util/mach/mach_message_server_test.cc index f5ccf42b..ca143fe9 100644 --- a/util/mach/mach_message_server_test.cc +++ b/util/mach/mach_message_server_test.cc @@ -17,6 +17,8 @@ #include #include +#include + #include "base/basictypes.h" #include "base/mac/scoped_mach_port.h" #include "gtest/gtest.h" @@ -222,7 +224,7 @@ class TestMachMessageServer : public MachMessageServer::Interface, EXPECT_EQ(RemotePort(), request->header.msgh_remote_port); } EXPECT_EQ(LocalPort(), request->header.msgh_local_port); - EXPECT_EQ(kRequestMessageId, request->header.msgh_id); + EXPECT_EQ(kRequestMessageID, request->header.msgh_id); if (options_.client_send_complex) { EXPECT_EQ(1u, request->body.msgh_descriptor_count); EXPECT_NE(kMachPortNull, request->port_descriptor.name); @@ -266,7 +268,7 @@ class TestMachMessageServer : public MachMessageServer::Interface, reply->Head.msgh_size = sizeof(*reply); reply->Head.msgh_remote_port = request->header.msgh_remote_port; reply->Head.msgh_local_port = MACH_PORT_NULL; - reply->Head.msgh_id = kReplyMessageId; + reply->Head.msgh_id = kReplyMessageID; reply->NDR = NDR_record; reply->RetCode = options_.server_mig_retcode; reply->number = replies_++; @@ -274,6 +276,12 @@ class TestMachMessageServer : public MachMessageServer::Interface, return true; } + std::set MachMessageServerRequestIDs() override { + const mach_msg_id_t request_ids[] = {kRequestMessageID}; + return std::set( + &request_ids[0], &request_ids[arraysize(request_ids)]); + } + mach_msg_size_t MachMessageServerRequestSize() override { return sizeof(RequestMessage); } @@ -469,7 +477,7 @@ class TestMachMessageServer : public MachMessageServer::Interface, break; } } - request.header.msgh_id = kRequestMessageId; + request.header.msgh_id = kRequestMessageID; if (options_.client_send_complex) { // Allocate a new receive right in this process and make a send right that // will appear in the parent process. This is used to test that the server @@ -534,7 +542,7 @@ class TestMachMessageServer : public MachMessageServer::Interface, ASSERT_EQ(sizeof(ReplyMessage), reply.Head.msgh_size); 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(kReplyMessageID, reply.Head.msgh_id); ASSERT_EQ(0, memcmp(&reply.NDR, &NDR_record, sizeof(NDR_record))); ASSERT_EQ(options_.server_mig_retcode, reply.RetCode); ASSERT_EQ(replies_, reply.number); @@ -584,16 +592,16 @@ class TestMachMessageServer : public MachMessageServer::Interface, static uint32_t requests_; static uint32_t replies_; - static const mach_msg_id_t kRequestMessageId = 16237; - static const mach_msg_id_t kReplyMessageId = kRequestMessageId + 100; + static const mach_msg_id_t kRequestMessageID = 16237; + static const mach_msg_id_t kReplyMessageID = kRequestMessageID + 100; DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer); }; uint32_t TestMachMessageServer::requests_; uint32_t TestMachMessageServer::replies_; -const mach_msg_id_t TestMachMessageServer::kRequestMessageId; -const mach_msg_id_t TestMachMessageServer::kReplyMessageId; +const mach_msg_id_t TestMachMessageServer::kRequestMessageID; +const mach_msg_id_t TestMachMessageServer::kReplyMessageID; TEST(MachMessageServer, Basic) { // The client sends one message to the server, which will wait indefinitely in diff --git a/util/util.gyp b/util/util.gyp index 70c0245a..e2c4c01a 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -45,6 +45,8 @@ 'mach/child_port_server.cc', 'mach/child_port_server.h', 'mach/child_port_types.h', + 'mach/composite_mach_message_server.cc', + 'mach/composite_mach_message_server.h', 'mach/exc_client_variants.cc', 'mach/exc_client_variants.h', 'mach/exc_server_variants.cc', @@ -240,6 +242,7 @@ 'mac/service_management_test.mm', 'mach/child_port_handshake_test.cc', 'mach/child_port_server_test.cc', + 'mach/composite_mach_message_server_test.cc', 'mach/exc_client_variants_test.cc', 'mach/exc_server_variants_test.cc', 'mach/exception_behaviors_test.cc',