// 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 "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() { } 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 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(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 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 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(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 < 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(reply.RetCode, kReturnCode0); 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(reply.RetCode, kReturnCode1); 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(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