Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1120)

Unified Diff: util/mach/mach_message_server_test.cc

Issue 555663002: Enhance the MachMessageServer test to cover port right ownership management (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « util/mach/mach_message_server.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: util/mach/mach_message_server_test.cc
diff --git a/util/mach/mach_message_server_test.cc b/util/mach/mach_message_server_test.cc
index dcbbae388d7d0c30df44ee1905feed75e27b2cc1..fe01cd1fd21f916985d75a7b548d65c7717b0cf0 100644
--- a/util/mach/mach_message_server_test.cc
+++ b/util/mach/mach_message_server_test.cc
@@ -60,10 +60,15 @@ class TestMachMessageServer : public MachMessageServer::Interface,
server_nonblocking(MachMessageServer::kBlocking),
server_timeout_ms(MACH_MSG_TIMEOUT_NONE),
server_mig_retcode(KERN_SUCCESS),
+ server_destroy_complex(true),
+ expect_server_destroyed_complex(true),
expect_server_result(KERN_SUCCESS),
client_send_request_count(1),
+ client_send_complex(false),
client_reply_port_type(kReplyPortNormal),
- child_send_all_requests_before_receiving_any_replies(false) {
+ client_expect_reply(true),
+ child_send_all_requests_before_receiving_any_replies(false),
+ child_wait_for_parent_pipe(false) {
}
// true if MachMessageServerFunction() is expected to be called.
@@ -90,28 +95,60 @@ class TestMachMessageServer : public MachMessageServer::Interface,
// a Mach RPC return value.
kern_return_t server_mig_retcode;
+ // The value that the server function should set its destroy_complex_request
+ // parameter to. This is true if resources sent in complex request messages
+ // should be destroyed, and false if they should not be destroyed, assuming
+ // that the server function indicates success.
+ bool server_destroy_complex;
+
+ // Whether to expect the server to destroy a complex message. Even if
+ // server_destroy_complex is false, a complex message will be destroyed if
+ // the MIG return code was unsuccessful.
+ bool expect_server_destroyed_complex;
+
// The expected return value from MachMessageServer::Run().
kern_return_t expect_server_result;
// The number of requests that the client should send to the server.
size_t client_send_request_count;
+ // true if the client should send a complex message, one that carries a port
+ // descriptor in its body. Normally false.
+ bool client_send_complex;
+
// The type of reply port that the client should provide in its request’s
// mach_msg_header_t::msgh_local_port, which will appear to the server as
// mach_msg_header_t::msgh_remote_port.
ReplyPortType client_reply_port_type;
+ // true if the client should wait for a reply from the server. For
+ // non-normal reply ports or requests which the server responds to with no
+ // reply (MIG_NO_REPLY), the server will either not send a reply or not
+ // succeed in sending a reply, and the child process should not wait for
+ // one.
+ bool client_expect_reply;
+
// true if the client should send all requests before attempting to receive
// any replies from the server. This is used for the persistent nonblocking
// test, which requires the client to fill the server’s queue before the
// server can attempt processing it.
bool child_send_all_requests_before_receiving_any_replies;
+
+ // true if the child should wait to receive a byte from the parent before
+ // exiting. This can be used to keep a receive right in the child alive
+ // until the parent has a chance to verify that it’s holding a send right.
+ // Otherwise, the right might appear in the parent as a dead name if the
+ // child exited before the parent had a chance to examine it. This would be
+ // a race.
+ bool child_wait_for_parent_pipe;
};
explicit TestMachMessageServer(const Options& options)
: MachMessageServer::Interface(),
MachMultiprocess(),
- options_(options) {
+ options_(options),
+ child_complex_message_port_(),
+ parent_complex_message_port_(MACH_PORT_NULL) {
}
// Runs the test.
@@ -131,7 +168,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
mach_msg_header_t* in,
mach_msg_header_t* out,
bool* destroy_complex_request) override {
- *destroy_complex_request = true;
+ *destroy_complex_request = options_.server_destroy_complex;
EXPECT_TRUE(options_.expect_server_interface_method_called);
if (!options_.expect_server_interface_method_called) {
@@ -144,15 +181,33 @@ class TestMachMessageServer : public MachMessageServer::Interface,
const ReceiveRequestMessage* request =
reinterpret_cast<ReceiveRequestMessage*>(in);
- EXPECT_EQ(static_cast<mach_msg_bits_t>(
- MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND)),
- request->header.msgh_bits);
+ const mach_msg_bits_t expect_msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |
+ (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);
+ EXPECT_EQ(expect_msgh_bits, request->header.msgh_bits);
EXPECT_EQ(sizeof(RequestMessage), request->header.msgh_size);
if (options_.client_reply_port_type == Options::kReplyPortNormal) {
EXPECT_EQ(RemotePort(), request->header.msgh_remote_port);
}
EXPECT_EQ(LocalPort(), request->header.msgh_local_port);
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);
+ 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);
+ EXPECT_EQ(
+ static_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR),
+ 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(0u, request->port_descriptor.disposition);
+ EXPECT_EQ(0u, request->port_descriptor.type);
+ }
EXPECT_EQ(0, memcmp(&request->ndr, &NDR_record, sizeof(NDR_record)));
EXPECT_EQ(requests_, request->number);
EXPECT_EQ(static_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0),
@@ -184,8 +239,8 @@ class TestMachMessageServer : public MachMessageServer::Interface,
}
private:
- struct RequestMessage {
- mach_msg_header_t header;
+ struct RequestMessage : public mach_msg_base_t {
+ mach_msg_port_descriptor_t port_descriptor;
NDR_record_t ndr;
uint32_t number;
};
@@ -214,6 +269,43 @@ class TestMachMessageServer : public MachMessageServer::Interface,
options_.server_nonblocking,
options_.server_timeout_ms)))
<< MachErrorMessage(kr, "MachMessageServer");
+
+ if (options_.client_send_complex) {
+ EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL),
+ parent_complex_message_port_);
+ mach_port_type_t type;
+
+ if (!options_.expect_server_destroyed_complex) {
+ // MachMessageServer should not have destroyed the resources sent in the
+ // complex request message.
+ kr = mach_port_type(
+ mach_task_self(), parent_complex_message_port_, &type);
+ EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type");
+ EXPECT_EQ(MACH_PORT_TYPE_SEND, type);
+
+ // Destroy the resources here.
+ kr = mach_port_deallocate(
+ mach_task_self(), parent_complex_message_port_);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_deallocate");
+ }
+
+ // The kernel won’t have reused the same name for another Mach port in
+ // this task so soon. It’s possible that something else in this task could
+ // have reused the name, but it’s unlikely for that to have happened in
+ // this test environment.
+ kr = mach_port_type(
+ mach_task_self(), parent_complex_message_port_, &type);
+ EXPECT_EQ(KERN_INVALID_NAME, kr)
+ << MachErrorMessage(kr, "mach_port_type");
+ }
+
+ if (options_.child_wait_for_parent_pipe) {
+ // Let the child know it’s safe to exit.
+ char c = '\0';
+ ssize_t rv = WriteFD(WritePipeFD(), &c, 1);
+ EXPECT_EQ(1, rv) << ErrnoMessage("write");
+ }
}
virtual void MachMultiprocessChild() override {
@@ -253,6 +345,13 @@ class TestMachMessageServer : public MachMessageServer::Interface,
}
}
}
+
+ if (options_.child_wait_for_parent_pipe) {
+ char c;
+ ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
+ ASSERT_EQ(1, rv) << ErrnoMessage("read");
+ ASSERT_EQ('\0', c);
+ }
}
// In the child process, sends a request message to the server.
@@ -264,7 +363,8 @@ class TestMachMessageServer : public MachMessageServer::Interface,
RequestMessage request = {};
request.header.msgh_bits =
- MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
+ (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);
request.header.msgh_size = sizeof(request);
request.header.msgh_remote_port = RemotePort();
kern_return_t kr;
@@ -290,8 +390,27 @@ class TestMachMessageServer : public MachMessageServer::Interface,
}
}
request.header.msgh_id = kRequestMessageId;
- request.number = requests_++;
+ 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
+ // properly handles ownership of resources received in complex messages.
+ request.body.msgh_descriptor_count = 1;
+ kr = mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &request.port_descriptor.name);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_allocate");
+ child_complex_message_port_.reset(request.port_descriptor.name);
+ request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND;
+ request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
+ } else {
+ request.body.msgh_descriptor_count = 0;
+ request.port_descriptor.name = MACH_PORT_NULL;
+ request.port_descriptor.disposition = 0;
+ request.port_descriptor.type = 0;
+ }
request.ndr = NDR_record;
+ request.number = requests_++;
kr = mach_msg(&request.header,
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
@@ -305,9 +424,10 @@ class TestMachMessageServer : public MachMessageServer::Interface,
// In the child process, waits for a reply message from the server.
void ChildWaitForReply() {
- if (options_.client_reply_port_type != Options::kReplyPortNormal) {
+ if (!options_.client_expect_reply) {
// The client shouldn’t expect a reply when it didn’t send a good reply
- // port with its request.
+ // port with its request, or when testing the server behaving in a way
+ // that doesn’t send replies.
return;
}
@@ -375,6 +495,16 @@ class TestMachMessageServer : public MachMessageServer::Interface,
const Options& options_;
+ // A receive right allocated in the child process. A send right will be
+ // created from this right and sent to the parent parent process in the
+ // request message.
+ base::mac::ScopedMachReceiveRight child_complex_message_port_;
+
+ // The send right received in the parent process. This right is stored in a
+ // member variable to test that resources carried in complex messages are
+ // properly destroyed in the server when expected.
+ mach_port_t parent_complex_message_port_;
+
static uint32_t requests_;
static uint32_t replies_;
@@ -482,8 +612,18 @@ TEST(MachMessageServer, ReturnCodeInvalidArgument) {
// This tests that the mig_reply_error_t::RetCode field is properly returned
// to the client.
TestMachMessageServer::Options options;
- TestMachMessageServer test_mach_message_server(options);
options.server_mig_retcode = KERN_INVALID_ARGUMENT;
Robert Sesek 2014/09/09 02:49:30 This is where my eyes glazed over in the last revi
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ReturnCodeNoReply) {
+ // This tests that when mig_reply_error_t::RetCode is set to MIG_NO_REPLY, no
+ // response is sent to the client.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = MIG_NO_REPLY;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
test_mach_message_server.Test();
}
@@ -492,9 +632,10 @@ TEST(MachMessageServer, ReplyPortNull) {
// this and avoid sending a message to the null port. No reply message is
// sent and the server returns success.
TestMachMessageServer::Options options;
- TestMachMessageServer test_mach_message_server(options);
options.client_reply_port_type =
TestMachMessageServer::Options::kReplyPortNull;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
test_mach_message_server.Test();
}
@@ -506,11 +647,72 @@ TEST(MachMessageServer, ReplyPortDead) {
// MACH_SEND_INVALID_DEST because it’s not possible to send a message to a
// dead name.
TestMachMessageServer::Options options;
- TestMachMessageServer test_mach_message_server(options);
options.parent_wait_for_child_pipe = true;
options.expect_server_result = MACH_SEND_INVALID_DEST;
options.client_reply_port_type =
TestMachMessageServer::Options::kReplyPortDead;
+ options.client_expect_reply = false;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, Complex) {
+ // The client allocates a new receive right and sends a complex request
+ // message to the server with a send right made out of this receive right. The
+ // server receives this message and is instructed to destroy the send right
+ // when it is done handling the request-reply transaction. The former send
+ // right is verified to be invalid after the server runs. This test ensures
+ // that resources transferred to a server process temporarily aren’t leaked.
+ TestMachMessageServer::Options options;
+ options.client_send_complex = true;
+ options.child_wait_for_parent_pipe = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexNotDestroyed) {
+ // As in MachMessageServer.Complex, but the server is instructed not to
+ // destroy the send right. After the server runs, the send right is verified
+ // to continue to exist in the server task. The client process is then
+ // signalled by pipe that it’s safe to exit so that the send right in the
+ // server task doesn’t prematurely become a dead name. This test ensures that
+ // rights that are expected to be retained in the server task are properly
+ // retained.
+ TestMachMessageServer::Options options;
+ options.server_destroy_complex = false;
+ options.expect_server_destroyed_complex = false;
+ options.client_send_complex = true;
+ options.child_wait_for_parent_pipe = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexDestroyedInvalidArgument) {
+ // As in MachMessageServer.ComplexNotDestroyed, but the server does not return
+ // a successful code via MIG. The server is expected to destroy resources in
+ // this case, because server_destroy_complex = false is only honored when a
+ // MIG request is handled successfully or with no reply.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = KERN_INVALID_TASK;
+ options.server_destroy_complex = false;
+ options.client_send_complex = true;
+ options.child_wait_for_parent_pipe = true;
+ TestMachMessageServer test_mach_message_server(options);
+ test_mach_message_server.Test();
+}
+
+TEST(MachMessageServer, ComplexNotDestroyedNoReply) {
+ // As in MachMessageServer.ComplexNotDestroyed, but the server does not send
+ // a reply message and is expected to retain the send right in the server
+ // task.
+ TestMachMessageServer::Options options;
+ options.server_mig_retcode = MIG_NO_REPLY;
+ options.server_destroy_complex = false;
+ options.expect_server_destroyed_complex = false;
+ options.client_send_complex = true;
+ options.client_expect_reply = false;
+ options.child_wait_for_parent_pipe = true;
+ TestMachMessageServer test_mach_message_server(options);
test_mach_message_server.Test();
}
« no previous file with comments | « util/mach/mach_message_server.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698