// 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/mach_message.h" #include #include #include "base/apple/scoped_mach_port.h" #include "build/build_config.h" #include "gtest/gtest.h" #include "test/mac/mach_errors.h" #include "util/mach/mach_extensions.h" #include "util/misc/implicit_cast.h" namespace crashpad { namespace test { namespace { TEST(MachMessage, MachMessageDeadlineFromTimeout) { MachMessageDeadline deadline_0 = MachMessageDeadlineFromTimeout(kMachMessageTimeoutNonblocking); EXPECT_EQ(deadline_0, kMachMessageDeadlineNonblocking); deadline_0 = MachMessageDeadlineFromTimeout(kMachMessageTimeoutWaitIndefinitely); EXPECT_EQ(deadline_0, kMachMessageDeadlineWaitIndefinitely); deadline_0 = MachMessageDeadlineFromTimeout(1); MachMessageDeadline deadline_1 = MachMessageDeadlineFromTimeout(100); EXPECT_NE(deadline_0, kMachMessageDeadlineNonblocking); EXPECT_NE(deadline_0, kMachMessageDeadlineWaitIndefinitely); EXPECT_NE(deadline_1, kMachMessageDeadlineNonblocking); EXPECT_NE(deadline_1, kMachMessageDeadlineWaitIndefinitely); EXPECT_GE(deadline_1, deadline_0); } TEST(MachMessage, PrepareMIGReplyFromRequest_SetMIGReplyError) { mach_msg_header_t request; request.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND); request.msgh_size = 64; request.msgh_remote_port = 0x01234567; request.msgh_local_port = 0x89abcdef; request.msgh_reserved = 0xa5a5a5a5; request.msgh_id = 1011; mig_reply_error_t reply; // PrepareMIGReplyFromRequest() doesn’t touch this field. reply.RetCode = MIG_TYPE_ERROR; PrepareMIGReplyFromRequest(&request, &reply.Head); EXPECT_EQ(reply.Head.msgh_bits, implicit_cast( MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0))); EXPECT_EQ(reply.Head.msgh_size, sizeof(reply)); EXPECT_EQ(reply.Head.msgh_remote_port, request.msgh_remote_port); EXPECT_EQ(reply.Head.msgh_local_port, kMachPortNull); EXPECT_EQ(reply.Head.msgh_reserved, 0u); EXPECT_EQ(reply.Head.msgh_id, 1111); EXPECT_EQ(reply.NDR.mig_vers, NDR_record.mig_vers); EXPECT_EQ(reply.NDR.if_vers, NDR_record.if_vers); EXPECT_EQ(reply.NDR.reserved1, NDR_record.reserved1); EXPECT_EQ(reply.NDR.mig_encoding, NDR_record.mig_encoding); EXPECT_EQ(reply.NDR.int_rep, NDR_record.int_rep); EXPECT_EQ(reply.NDR.char_rep, NDR_record.char_rep); EXPECT_EQ(reply.NDR.float_rep, NDR_record.float_rep); EXPECT_EQ(reply.NDR.reserved2, NDR_record.reserved2); EXPECT_EQ(reply.RetCode, MIG_TYPE_ERROR); SetMIGReplyError(&reply.Head, MIG_BAD_ID); EXPECT_EQ(reply.RetCode, MIG_BAD_ID); } TEST(MachMessage, MachMessageTrailerFromHeader) { mach_msg_empty_t empty; empty.send.header.msgh_size = sizeof(mach_msg_empty_send_t); EXPECT_EQ(MachMessageTrailerFromHeader(&empty.rcv.header), &empty.rcv.trailer); struct TestSendMessage : public mach_msg_header_t { uint8_t data[126]; }; struct TestReceiveMessage : public TestSendMessage { mach_msg_trailer_t trailer; }; union TestMessage { TestSendMessage send; TestReceiveMessage receive; }; TestMessage test; test.send.msgh_size = sizeof(TestSendMessage); EXPECT_EQ(MachMessageTrailerFromHeader(&test.receive), &test.receive.trailer); } TEST(MachMessage, MachMessageDestroyReceivedPort) { mach_port_t port = NewMachPort(MACH_PORT_RIGHT_RECEIVE); ASSERT_NE(port, kMachPortNull); EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE)); base::apple::ScopedMachReceiveRight receive( NewMachPort(MACH_PORT_RIGHT_RECEIVE)); mach_msg_type_name_t right_type; kern_return_t kr = mach_port_extract_right(mach_task_self(), receive.get(), MACH_MSG_TYPE_MAKE_SEND, &port, &right_type); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_extract_right"); ASSERT_EQ(port, receive); ASSERT_EQ(right_type, implicit_cast(MACH_MSG_TYPE_PORT_SEND)); EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND)); kr = mach_port_extract_right(mach_task_self(), receive.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE, &port, &right_type); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_extract_right"); ASSERT_NE(port, kMachPortNull); EXPECT_NE(port, receive); ASSERT_EQ(right_type, implicit_cast(MACH_MSG_TYPE_PORT_SEND_ONCE)); EXPECT_TRUE( MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND_ONCE)); kr = mach_port_extract_right(mach_task_self(), receive.get(), MACH_MSG_TYPE_MAKE_SEND, &port, &right_type); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_extract_right"); ASSERT_EQ(port, receive); ASSERT_EQ(right_type, implicit_cast(MACH_MSG_TYPE_PORT_SEND)); EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE)); std::ignore = receive.release(); EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND)); } #if BUILDFLAG(IS_MAC) TEST(MachMessage, AuditPIDFromMachMessageTrailer) { base::apple::ScopedMachReceiveRight port( NewMachPort(MACH_PORT_RIGHT_RECEIVE)); ASSERT_NE(port, kMachPortNull); mach_msg_empty_send_t send = {}; send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); send.header.msgh_size = sizeof(send); send.header.msgh_remote_port = port.get(); mach_msg_return_t mr = MachMessageWithDeadline(&send.header, MACH_SEND_MSG, 0, MACH_PORT_NULL, kMachMessageDeadlineNonblocking, MACH_PORT_NULL, false); ASSERT_EQ(mr, MACH_MSG_SUCCESS) << MachErrorMessage(mr, "MachMessageWithDeadline send"); struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t { union { mach_msg_trailer_t trailer; mach_msg_audit_trailer_t audit_trailer; }; }; EmptyReceiveMessageWithAuditTrailer receive; mr = MachMessageWithDeadline(&receive.header, MACH_RCV_MSG | kMachMessageReceiveAuditTrailer, sizeof(receive), port.get(), kMachMessageDeadlineNonblocking, MACH_PORT_NULL, false); ASSERT_EQ(mr, MACH_MSG_SUCCESS) << MachErrorMessage(mr, "MachMessageWithDeadline receive"); EXPECT_EQ(AuditPIDFromMachMessageTrailer(&receive.trailer), getpid()); } #endif // BUILDFLAG(IS_MAC) } // namespace } // namespace test } // namespace crashpad