| // Copyright 2014 The Crashpad Authors |
| // |
| // 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 <sys/types.h> |
| |
| #include <iterator> |
| |
| #include "base/strings/stringprintf.h" |
| #include "gtest/gtest.h" |
| #include "test/gtest_death.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(server.MachMessageServerRequestSize(), sizeof(request.header)); |
| |
| mig_reply_error_t reply = {}; |
| EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply)); |
| |
| bool destroy_complex_request = false; |
| EXPECT_FALSE(server.MachMessageServerFunction( |
| &request.header, &reply.Head, &destroy_complex_request)); |
| EXPECT_EQ(reply.RetCode, MIG_BAD_ID); |
| } |
| |
| 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(const TestMachMessageHandler&) = delete; |
| TestMachMessageHandler& operator=(const TestMachMessageHandler&) = delete; |
| |
| ~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_.find(in->msgh_id), request_ids_.end()); |
| |
| *destroy_complex_request = destroy_complex_request_; |
| PrepareMIGReplyFromRequest(in, out); |
| SetMIGReplyError(out, return_code_); |
| return return_value_; |
| } |
| |
| std::set<mach_msg_id_t> 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<mach_msg_id_t> 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_; |
| }; |
| |
| TEST(CompositeMachMessageServer, HandlerDoesNotHandle) { |
| TestMachMessageHandler handler; |
| |
| CompositeMachMessageServer server; |
| server.AddHandler(&handler); |
| |
| EXPECT_TRUE(server.MachMessageServerRequestIDs().empty()); |
| |
| mach_msg_empty_rcv_t request = {}; |
| EXPECT_EQ(server.MachMessageServerRequestSize(), sizeof(request.header)); |
| |
| mig_reply_error_t reply = {}; |
| EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply)); |
| |
| bool destroy_complex_request = false; |
| EXPECT_FALSE(server.MachMessageServerFunction( |
| &request.header, &reply.Head, &destroy_complex_request)); |
| EXPECT_EQ(reply.RetCode, MIG_BAD_ID); |
| EXPECT_FALSE(destroy_complex_request); |
| } |
| |
| TEST(CompositeMachMessageServer, OneHandler) { |
| constexpr mach_msg_id_t kRequestID = 100; |
| constexpr mach_msg_size_t kRequestSize = 256; |
| constexpr mach_msg_size_t kReplySize = 128; |
| constexpr 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<mach_msg_id_t> expect_request_ids; |
| expect_request_ids.insert(kRequestID); |
| EXPECT_EQ(server.MachMessageServerRequestIDs(), expect_request_ids); |
| |
| EXPECT_EQ(server.MachMessageServerRequestSize(), kRequestSize); |
| EXPECT_EQ(server.MachMessageServerReplySize(), kReplySize); |
| |
| 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(reply.RetCode, MIG_BAD_ID); |
| 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(reply.RetCode, kReturnCode); |
| EXPECT_TRUE(destroy_complex_request); |
| } |
| |
| TEST(CompositeMachMessageServer, ThreeHandlers) { |
| static constexpr mach_msg_id_t kRequestIDs0[] = {5}; |
| constexpr kern_return_t kReturnCode0 = KERN_SUCCESS; |
| |
| static constexpr mach_msg_id_t kRequestIDs1[] = {4, 7}; |
| constexpr kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE; |
| |
| static constexpr mach_msg_id_t kRequestIDs2[] = {10, 0, 20}; |
| constexpr mach_msg_size_t kRequestSize2 = 6144; |
| constexpr mach_msg_size_t kReplySize2 = 16384; |
| constexpr kern_return_t kReturnCode2 = KERN_NOT_RECEIVER; |
| |
| TestMachMessageHandler handlers[3]; |
| std::set<mach_msg_id_t> expect_request_ids; |
| |
| for (size_t index = 0; index < std::size(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 < std::size(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 < std::size(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(server.MachMessageServerRequestIDs(), expect_request_ids); |
| |
| EXPECT_EQ(server.MachMessageServerRequestSize(), kRequestSize2); |
| EXPECT_EQ(server.MachMessageServerReplySize(), kReplySize2); |
| |
| 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(reply.RetCode, MIG_BAD_ID); |
| EXPECT_FALSE(destroy_complex_request); |
| |
| // Send messages with known request IDs. |
| |
| for (size_t index = 0; index < std::size(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(reply.RetCode, kReturnCode0); |
| EXPECT_FALSE(destroy_complex_request); |
| } |
| |
| for (size_t index = 0; index < std::size(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(reply.RetCode, kReturnCode1); |
| EXPECT_TRUE(destroy_complex_request); |
| } |
| |
| for (size_t index = 0; index < std::size(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(reply.RetCode, kReturnCode2); |
| EXPECT_TRUE(destroy_complex_request); |
| } |
| } |
| |
| // CompositeMachMessageServer can’t deal with two handlers that want to handle |
| // the same request ID. |
| TEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) { |
| constexpr 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_CHECK(server.AddHandler(&handlers[1]), "duplicate request ID"); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |