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

Unified Diff: mojo/edk/system/mach_port_relay.cc

Issue 1712143002: [mojo-edk] Add support for transferring mach ports. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More stuff. Created 4 years, 9 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
Index: mojo/edk/system/mach_port_relay.cc
diff --git a/mojo/edk/system/mach_port_relay.cc b/mojo/edk/system/mach_port_relay.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4e01f6c9252766f56dc0fae25d781af6e3c121df
--- /dev/null
+++ b/mojo/edk/system/mach_port_relay.cc
@@ -0,0 +1,271 @@
+// Copyright 2016 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 "mojo/edk/system/mach_port_relay.h"
+
+#include <fcntl.h>
+#include <mach/mach.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/process/process.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// Struct for sending a complex Mach message.
+struct MachSendComplexMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t data;
+};
+
+// Struct for receiving a complex message.
+struct MachReceiveComplexMessage : public MachSendComplexMessage {
+ mach_msg_trailer_t trailer;
+};
+
+// Sends a Mach port to |endpoint|. Assumes that |endpoint| is a send once
+// right. Takes ownership of |endpoint|.
+kern_return_t SendMachPort(mach_port_t endpoint,
erikchen 2016/03/09 19:13:17 Can we move everything in this anonymous namespace
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done. Actually done in https://codereview.chromium
+ mach_port_t port_to_send,
+ int disposition) {
+ MachSendComplexMessage send_msg;
+ send_msg.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX;
+ send_msg.header.msgh_size = sizeof(send_msg);
+ send_msg.header.msgh_remote_port = endpoint;
+ send_msg.header.msgh_local_port = MACH_PORT_NULL;
+ send_msg.header.msgh_reserved = 0;
+ send_msg.header.msgh_id = 0;
+ send_msg.body.msgh_descriptor_count = 1;
+ send_msg.data.name = port_to_send;
+ send_msg.data.disposition = disposition;
+ send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
+
+ kern_return_t kr =
+ mach_msg(&send_msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ send_msg.header.msgh_size,
+ 0, // receive limit
+ MACH_PORT_NULL, // receive name
+ 0, // timeout
+ MACH_PORT_NULL); // notification port
+
+ if (kr != KERN_SUCCESS)
+ mach_port_deallocate(mach_task_self(), endpoint);
+
+ return kr;
+}
+
+// Receives a Mach port from |port_to_listen_on|, which should have exactly one
+// queued message. Returns |MACH_PORT_NULL| on any error.
+base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on) {
+ MachReceiveComplexMessage recv_msg;
+ mach_msg_header_t* recv_hdr = &recv_msg.header;
+ recv_hdr->msgh_local_port = port_to_listen_on;
+ recv_hdr->msgh_size = sizeof(recv_msg);
+
+ kern_return_t kr =
+ mach_msg(recv_hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
+ recv_hdr->msgh_size, port_to_listen_on, 0, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ if (recv_msg.header.msgh_id != 0)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ return base::mac::ScopedMachSendRight(recv_msg.data.name);
+}
+
+mach_port_name_t CreateIntermediateMachPort(
+ mach_port_t task_port,
+ base::mac::ScopedMachSendRight port_to_insert) {
+ DCHECK_NE(mach_task_self(), task_port);
+ DCHECK_NE(static_cast<mach_port_name_t>(MACH_PORT_NULL), task_port);
+
+ // Make a port with receive rights in the destination task.
+ mach_port_name_t endpoint;
+ kern_return_t kr =
+ mach_port_allocate(task_port, MACH_PORT_RIGHT_RECEIVE, &endpoint);
+ if (kr != KERN_SUCCESS) {
+ LOG(ERROR) << "Unable to allocate port with receive right";
+ return MACH_PORT_NULL;
+ }
+
+ // Change its message queue limit so that it accepts one message.
+ mach_port_limits limits = {};
+ limits.mpl_qlimit = 1;
+ kr = mach_port_set_attributes(task_port, endpoint, MACH_PORT_LIMITS_INFO,
+ reinterpret_cast<mach_port_info_t>(&limits),
+ MACH_PORT_LIMITS_INFO_COUNT);
+ if (kr != KERN_SUCCESS) {
+ LOG(ERROR) << "Unable to set port attributes";
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+
+ // Get a send right.
+ mach_port_t send_once_right;
+ mach_msg_type_name_t send_right_type;
+ kr =
+ mach_port_extract_right(task_port, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &send_once_right, &send_right_type);
+ if (kr != KERN_SUCCESS) {
+ LOG(ERROR) << "Unable to extra port send right";
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+ DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE),
+ send_right_type);
+
+ // This call takes ownership of |send_once_right|.
+ kr = SendMachPort(
+ send_once_right, port_to_insert.get(), MACH_MSG_TYPE_COPY_SEND);
+ if (kr != KERN_SUCCESS) {
+ LOG(ERROR) << "Unable to send mach port";
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+
+ // Endpoint is intentionally leaked into the destination task. An IPC must be
+ // sent to the destination task so that it can clean up this port.
+ return endpoint;
+}
+
+} // namespace
+
+// static
+bool MachPortRelay::ReceivePorts(PlatformHandleVector* handles) {
+ DCHECK(handles);
+
+ size_t num_received = 0;
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = handles->data() + i;
+ if (handle->type != PlatformHandle::Type::MACH)
+ continue;
+
+ base::mac::ScopedMachReceiveRight message_port(handle->port);
erikchen 2016/03/09 19:13:17 This method takes ownership of handle->port, and r
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done.
+ base::mac::ScopedMachSendRight received_port(
+ ReceiveMachPort(message_port.get()));
+ if (received_port.get() == MACH_PORT_NULL) {
+ LOG(ERROR) << "Error receiving mach port";
+ return false;
erikchen 2016/03/09 19:13:17 clear handle->port?
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done.
+ }
+
+ num_received++;
erikchen 2016/03/09 19:13:17 this variable is never used.
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done.
+ handle->port = received_port.release();
+ }
+
+ return true;
+}
+
+MachPortRelay::MachPortRelay(base::PortProvider* port_provider)
+ : port_provider_(port_provider) {
+ DCHECK(port_provider);
+ port_provider_->AddObserver(this);
+}
+
+MachPortRelay::~MachPortRelay() {
+ port_provider_->RemoveObserver(this);
+}
+
+bool MachPortRelay::SendPortsToProcess(Channel::Message* message,
+ base::ProcessHandle process) {
+ DCHECK(message);
+ mach_port_t task_port = port_provider_->TaskForPid(process);
+ if (task_port == MACH_PORT_NULL)
+ return false;
+
+ size_t num_sent = 0;
+ bool error = false;
+ ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+ // Message should have handles, otherwise there's no point in calling this
+ // function.
+ DCHECK(handles);
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = &(*handles)[i];
+ if (handle->type != PlatformHandle::Type::MACH)
+ continue;
+
+ mach_port_name_t intermediate_port;
+ DCHECK(handle->port != MACH_PORT_NULL);
+ intermediate_port = CreateIntermediateMachPort(
+ task_port, base::mac::ScopedMachSendRight(handle->port));
+ if (intermediate_port == MACH_PORT_NULL) {
erikchen 2016/03/09 19:13:17 handle->port should be set to MACH_PORT_NULL?
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done.
+ error = true;
+ break;
+ }
+ handle->port = intermediate_port;
erikchen 2016/03/09 19:13:17 we need some way of leaking this handle from the c
Anand Mistry (off Chromium) 2016/03/11 07:44:18 This is tricky because there's a mixture of "real"
+ num_sent++;
+ }
+ DCHECK(error || num_sent);
+ message->SetHandles(std::move(handles));
+
+ return !error;
+}
+
+bool MachPortRelay::ExtractPortRights(Channel::Message* message,
+ base::ProcessHandle process) {
+ DCHECK(message);
+
+ mach_port_t task_port = port_provider_->TaskForPid(process);
+ if (task_port == MACH_PORT_NULL)
+ return false;
+
+ size_t num_received = 0;
+ bool error = false;
+ ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+ // Message should have handles, otherwise there's no point in calling this
+ // function.
+ DCHECK(handles);
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = handles->data() + i;
+ if (handle->type != PlatformHandle::Type::MACH)
+ continue;
+
+ mach_port_t extracted_right = MACH_PORT_NULL;
+ mach_msg_type_name_t extracted_right_type;
+ kern_return_t kr =
+ mach_port_extract_right(task_port, handle->port,
+ MACH_MSG_TYPE_MOVE_SEND,
+ &extracted_right, &extracted_right_type);
+ if (kr != KERN_SUCCESS) {
+ error = true;
+ break;
+ }
+
+ DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
+ extracted_right_type);
+ handle->port = extracted_right;
+ num_received++;
+ }
+ DCHECK(error || num_received);
+ message->SetHandles(std::move(handles));
+
+ return !error;
+}
+
+void MachPortRelay::AddObserver(Observer* observer) {
+ base::AutoLock locker(observers_lock_);
+ bool inserted = observers_.insert(observer).second;
+ DCHECK(inserted);
+}
+
+void MachPortRelay::RemoveObserver(Observer* observer) {
+ base::AutoLock locker(observers_lock_);
+ observers_.erase(observer);
+}
+
+void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) {
+ base::AutoLock locker(observers_lock_);
+ for (const auto observer : observers_)
+ observer->OnProcessReady(process);
+}
+
+} // namespace edk
+} // namespace mojo

Powered by Google App Engine
This is Rietveld 408576698