diff options
author | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 06:36:54 +0000 |
---|---|---|
committer | rsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 06:36:54 +0000 |
commit | 6e9bfe1e87c80238666d32878e18322e108d8700 (patch) | |
tree | aa1899cb7109d5c9edbd44b68d601342e5c4fef6 /sandbox | |
parent | ff84e05ab824d27fc77890028afa15b2635ff4cd (diff) | |
download | chromium_src-6e9bfe1e87c80238666d32878e18322e108d8700.zip chromium_src-6e9bfe1e87c80238666d32878e18322e108d8700.tar.gz chromium_src-6e9bfe1e87c80238666d32878e18322e108d8700.tar.bz2 |
Create DispatchSourceMach to run a MACH_RECV dispatch source.
This implements RAII semantics so that when the destructor runs, the source
is cancelled and event handler blocks cannot call methods on a destructed
object.
BUG=382931
R=mark@chromium.org
Review URL: https://codereview.chromium.org/392273002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283664 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/mac/BUILD.gn | 3 | ||||
-rw-r--r-- | sandbox/mac/dispatch_source_mach.cc | 62 | ||||
-rw-r--r-- | sandbox/mac/dispatch_source_mach.h | 61 | ||||
-rw-r--r-- | sandbox/mac/dispatch_source_mach_unittest.cc | 119 | ||||
-rw-r--r-- | sandbox/mac/mach_message_server.cc | 26 | ||||
-rw-r--r-- | sandbox/mac/mach_message_server.h | 17 | ||||
-rw-r--r-- | sandbox/mac/sandbox_mac.gypi | 3 |
7 files changed, 258 insertions, 33 deletions
diff --git a/sandbox/mac/BUILD.gn b/sandbox/mac/BUILD.gn index 2da2e17..20b3f00 100644 --- a/sandbox/mac/BUILD.gn +++ b/sandbox/mac/BUILD.gn @@ -6,6 +6,8 @@ component("sandbox") { sources = [ "bootstrap_sandbox.cc", "bootstrap_sandbox.h", + "dispatch_source_mach.cc", + "dispatch_source_mach.h", "launchd_interception_server.cc", "launchd_interception_server.h", "mach_message_server.cc", @@ -56,6 +58,7 @@ action("generate_stubs") { test("sandbox_mac_unittests") { sources = [ "bootstrap_sandbox_unittest.mm", + "dispatch_source_mach_unittest.cc", "policy_unittest.cc", ] diff --git a/sandbox/mac/dispatch_source_mach.cc b/sandbox/mac/dispatch_source_mach.cc new file mode 100644 index 0000000..a6f3ac8 --- /dev/null +++ b/sandbox/mac/dispatch_source_mach.cc @@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/mac/dispatch_source_mach.h" + +namespace sandbox { + +DispatchSourceMach::DispatchSourceMach(const char* name, + mach_port_t port, + void (^event_handler)()) + // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL + // means the same thing but is not symbolically clear. + : DispatchSourceMach(dispatch_queue_create(name, NULL), + port, + event_handler) { + // Since the queue was created above in the delegated constructor, and it was + // subsequently retained, release it here. + dispatch_release(queue_); +} + +DispatchSourceMach::DispatchSourceMach(dispatch_queue_t queue, + mach_port_t port, + void (^event_handler)()) + : queue_(queue), + source_(dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, + port, 0, queue_)), + source_canceled_(dispatch_semaphore_create(0)) { + dispatch_retain(queue); + + dispatch_source_set_event_handler(source_, event_handler); + dispatch_source_set_cancel_handler(source_, ^{ + dispatch_semaphore_signal(source_canceled_); + }); +} + +DispatchSourceMach::~DispatchSourceMach() { + Cancel(); +} + +void DispatchSourceMach::Resume() { + dispatch_resume(source_); +} + +void DispatchSourceMach::Cancel() { + if (source_) { + dispatch_source_cancel(source_); + dispatch_release(source_); + source_ = NULL; + + dispatch_semaphore_wait(source_canceled_, DISPATCH_TIME_FOREVER); + dispatch_release(source_canceled_); + source_canceled_ = NULL; + } + + if (queue_) { + dispatch_release(queue_); + queue_ = NULL; + } +} + +} // namespace sandbox diff --git a/sandbox/mac/dispatch_source_mach.h b/sandbox/mac/dispatch_source_mach.h new file mode 100644 index 0000000..e7234f8 --- /dev/null +++ b/sandbox/mac/dispatch_source_mach.h @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_MAC_DISPATCH_SOURCE_MACH_H_ +#define SANDBOX_MAC_DISPATCH_SOURCE_MACH_H_ + +#include <dispatch/dispatch.h> + +#include "base/basictypes.h" +#include "sandbox/sandbox_export.h" + +namespace sandbox { + +// This class encapsulates a MACH_RECV dispatch source. When this object is +// destroyed, the source will be cancelled and it will wait for the source +// to stop executing work. The source can run on either a user-supplied queue, +// or it can create its own for the source. +class SANDBOX_EXPORT DispatchSourceMach { + public: + // Creates a new dispatch source for the |port| and schedules it on a new + // queue that will be created with |name|. When a Mach message is received, + // the |event_handler| will be called. + DispatchSourceMach(const char* name, + mach_port_t port, + void (^event_handler)()); + + // Creates a new dispatch source with the same semantics as above, but rather + // than creating a new queue, it schedules the source on |queue|. + DispatchSourceMach(dispatch_queue_t queue, + mach_port_t port, + void (^event_handler)()); + + // Cancels the source and waits for it to become fully cancelled before + // releasing the source. + ~DispatchSourceMach(); + + // Resumes the source. This must be called before any Mach messages will + // be received. + void Resume(); + + private: + // Cancels the source, after which this class will no longer receive Mach + // messages. Calling this more than once is a no-op. + void Cancel(); + + // The dispatch queue used to service the source_. + dispatch_queue_t queue_; + + // A MACH_RECV dispatch source. + dispatch_source_t source_; + + // Semaphore used to wait on the |source_|'s cancellation in the destructor. + dispatch_semaphore_t source_canceled_; + + DISALLOW_COPY_AND_ASSIGN(DispatchSourceMach); +}; + +} // namespace sandbox + +#endif // SANDBOX_MAC_DISPATCH_SOURCE_MACH_H_ diff --git a/sandbox/mac/dispatch_source_mach_unittest.cc b/sandbox/mac/dispatch_source_mach_unittest.cc new file mode 100644 index 0000000..742f14e --- /dev/null +++ b/sandbox/mac/dispatch_source_mach_unittest.cc @@ -0,0 +1,119 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/mac/dispatch_source_mach.h" + +#include <mach/mach.h> + +#include "base/logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/test_timeouts.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +class DispatchSourceMachTest : public testing::Test { + public: + virtual void SetUp() OVERRIDE { + mach_port_t port = MACH_PORT_NULL; + ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(), + MACH_PORT_RIGHT_RECEIVE, &port)); + receive_right_.reset(port); + + ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(mach_task_self(), port, + port, MACH_MSG_TYPE_MAKE_SEND)); + send_right_.reset(port); + } + + mach_port_t port() { return receive_right_.get(); } + + void WaitForSemaphore(dispatch_semaphore_t semaphore) { + dispatch_semaphore_wait(semaphore, dispatch_time( + DISPATCH_TIME_NOW, + TestTimeouts::action_timeout().InSeconds() * NSEC_PER_SEC)); + } + + private: + base::mac::ScopedMachReceiveRight receive_right_; + base::mac::ScopedMachSendRight send_right_; +}; + +TEST_F(DispatchSourceMachTest, ReceiveAfterResume) { + dispatch_semaphore_t signal = dispatch_semaphore_create(0); + + bool __block did_receive = false; + DispatchSourceMach source("org.chromium.sandbox.test.ReceiveAfterResume", + port(), ^{ + mach_msg_empty_rcv_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port(); + mach_msg_receive(&msg.header); + did_receive = true; + + dispatch_semaphore_signal(signal); + }); + + mach_msg_empty_send_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port(); + msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); + ASSERT_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); + + EXPECT_FALSE(did_receive); + + source.Resume(); + + WaitForSemaphore(signal); + + EXPECT_TRUE(did_receive); +} + +TEST_F(DispatchSourceMachTest, NoMessagesAfterDestruction) { + scoped_ptr<int> count(new int(0)); + int* __block count_ptr = count.get(); + + scoped_ptr<DispatchSourceMach> source(new DispatchSourceMach( + "org.chromium.sandbox.test.NoMessagesAfterDestruction", + port(), ^{ + mach_msg_empty_rcv_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port(); + mach_msg_receive(&msg.header); + LOG(INFO) << "Receieve " << *count_ptr; + ++(*count_ptr); + })); + source->Resume(); + + dispatch_queue_t queue = + dispatch_queue_create("org.chromium.sandbox.test.MessageSend", NULL); + dispatch_semaphore_t signal = dispatch_semaphore_create(0); + for (int i = 0; i < 30; ++i) { + dispatch_async(queue, ^{ + mach_msg_empty_send_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port(); + msg.header.msgh_bits = + MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); + mach_msg_send(&msg.header); + }); + + // After sending five messages, shut down the source and taint the + // pointer the handler dereferences. The test will crash if |count_ptr| + // is being used after "free". + if (i == 5) { + scoped_ptr<DispatchSourceMach>* source_ptr = &source; + dispatch_async(queue, ^{ + source_ptr->reset(); + count_ptr = reinterpret_cast<int*>(0xdeaddead); + dispatch_semaphore_signal(signal); + }); + } + } + + WaitForSemaphore(signal); + dispatch_release(queue); +} + +} // namespace sandbox diff --git a/sandbox/mac/mach_message_server.cc b/sandbox/mac/mach_message_server.cc index cbd9916..5a73357 100644 --- a/sandbox/mac/mach_message_server.cc +++ b/sandbox/mac/mach_message_server.cc @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/mac/mach_logging.h" #include "base/strings/stringprintf.h" +#include "sandbox/mac/dispatch_source_mach.h" namespace sandbox { @@ -21,9 +22,6 @@ MachMessageServer::MachMessageServer( mach_msg_size_t buffer_size) : demuxer_(demuxer), server_port_(server_receive_right), - server_queue_(NULL), - server_source_(NULL), - source_canceled_(dispatch_semaphore_create(0)), buffer_size_( mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))), did_forward_message_(false) { @@ -31,15 +29,6 @@ MachMessageServer::MachMessageServer( } MachMessageServer::~MachMessageServer() { - if (server_source_) { - dispatch_source_cancel(server_source_); - dispatch_release(server_source_); - - dispatch_semaphore_wait(source_canceled_, DISPATCH_TIME_FOREVER); - dispatch_release(source_canceled_); - } - if (server_queue_) - dispatch_release(server_queue_); } bool MachMessageServer::Initialize() { @@ -78,18 +67,11 @@ bool MachMessageServer::Initialize() { reply_buffer_.reset(buffer, buffer_size_); // Set up the dispatch queue to service the bootstrap port. - // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL means - // the same thing but is not symbolically clear. std::string label = base::StringPrintf( "org.chromium.sandbox.MachMessageServer.%p", demuxer_); - server_queue_ = dispatch_queue_create(label.c_str(), NULL); - server_source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, - server_port_.get(), 0, server_queue_); - dispatch_source_set_event_handler(server_source_, ^{ ReceiveMessage(); }); - dispatch_source_set_cancel_handler(server_source_, ^{ - dispatch_semaphore_signal(source_canceled_); - }); - dispatch_resume(server_source_); + dispatch_source_.reset(new DispatchSourceMach( + label.c_str(), server_port_.get(), ^{ ReceiveMessage(); })); + dispatch_source_->Resume(); return true; } diff --git a/sandbox/mac/mach_message_server.h b/sandbox/mac/mach_message_server.h index ba0b2c4..95be5fb 100644 --- a/sandbox/mac/mach_message_server.h +++ b/sandbox/mac/mach_message_server.h @@ -5,15 +5,17 @@ #ifndef SANDBOX_MAC_MACH_MESSAGE_SERVER_H_ #define SANDBOX_MAC_MACH_MESSAGE_SERVER_H_ -#include <dispatch/dispatch.h> #include <mach/mach.h> #include "base/mac/scoped_mach_port.h" #include "base/mac/scoped_mach_vm.h" +#include "base/memory/scoped_ptr.h" #include "sandbox/mac/message_server.h" namespace sandbox { +class DispatchSourceMach; + // A Mach message server that operates a receive port. Messages are received // and then passed to the MessageDemuxer for handling. The Demuxer // can use the server class to send a reply, forward the message to a @@ -53,16 +55,6 @@ class MachMessageServer : public MessageServer { // The Mach port on which the server is receiving requests. base::mac::ScopedMachReceiveRight server_port_; - // The dispatch queue used to service the server_source_. - dispatch_queue_t server_queue_; - - // A MACH_RECV dispatch source for the server_port_. - dispatch_source_t server_source_; - - // Semaphore used to wait on the |server_source_|'s cancellation in the - // destructor. - dispatch_semaphore_t source_canceled_; - // The size of the two message buffers below. const mach_msg_size_t buffer_size_; @@ -70,6 +62,9 @@ class MachMessageServer : public MessageServer { base::mac::ScopedMachVM request_buffer_; base::mac::ScopedMachVM reply_buffer_; + // MACH_RECV dispatch source that handles the |server_port_|. + scoped_ptr<DispatchSourceMach> dispatch_source_; + // Whether or not ForwardMessage() was called during ReceiveMessage(). bool did_forward_message_; }; diff --git a/sandbox/mac/sandbox_mac.gypi b/sandbox/mac/sandbox_mac.gypi index b2c1495..c33243b 100644 --- a/sandbox/mac/sandbox_mac.gypi +++ b/sandbox/mac/sandbox_mac.gypi @@ -10,6 +10,8 @@ 'sources': [ 'bootstrap_sandbox.cc', 'bootstrap_sandbox.h', + 'dispatch_source_mach.cc', + 'dispatch_source_mach.h', 'launchd_interception_server.cc', 'launchd_interception_server.h', 'mach_message_server.cc', @@ -79,6 +81,7 @@ 'type': 'executable', 'sources': [ 'bootstrap_sandbox_unittest.mm', + 'dispatch_source_mach_unittest.cc', 'policy_unittest.cc', ], 'dependencies': [ |