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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "mojo/edk/system/mach_port_relay.h"
6
7 #include <fcntl.h>
8 #include <mach/mach.h>
9 #include <unistd.h>
10
11 #include <utility>
12
13 #include "base/logging.h"
14 #include "base/mac/scoped_mach_port.h"
15 #include "base/process/process.h"
16 #include "mojo/edk/embedder/platform_handle_vector.h"
17
18 namespace mojo {
19 namespace edk {
20
21 namespace {
22
23 // Struct for sending a complex Mach message.
24 struct MachSendComplexMessage {
25 mach_msg_header_t header;
26 mach_msg_body_t body;
27 mach_msg_port_descriptor_t data;
28 };
29
30 // Struct for receiving a complex message.
31 struct MachReceiveComplexMessage : public MachSendComplexMessage {
32 mach_msg_trailer_t trailer;
33 };
34
35 // Sends a Mach port to |endpoint|. Assumes that |endpoint| is a send once
36 // right. Takes ownership of |endpoint|.
37 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
38 mach_port_t port_to_send,
39 int disposition) {
40 MachSendComplexMessage send_msg;
41 send_msg.header.msgh_bits =
42 MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX;
43 send_msg.header.msgh_size = sizeof(send_msg);
44 send_msg.header.msgh_remote_port = endpoint;
45 send_msg.header.msgh_local_port = MACH_PORT_NULL;
46 send_msg.header.msgh_reserved = 0;
47 send_msg.header.msgh_id = 0;
48 send_msg.body.msgh_descriptor_count = 1;
49 send_msg.data.name = port_to_send;
50 send_msg.data.disposition = disposition;
51 send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
52
53 kern_return_t kr =
54 mach_msg(&send_msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
55 send_msg.header.msgh_size,
56 0, // receive limit
57 MACH_PORT_NULL, // receive name
58 0, // timeout
59 MACH_PORT_NULL); // notification port
60
61 if (kr != KERN_SUCCESS)
62 mach_port_deallocate(mach_task_self(), endpoint);
63
64 return kr;
65 }
66
67 // Receives a Mach port from |port_to_listen_on|, which should have exactly one
68 // queued message. Returns |MACH_PORT_NULL| on any error.
69 base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on) {
70 MachReceiveComplexMessage recv_msg;
71 mach_msg_header_t* recv_hdr = &recv_msg.header;
72 recv_hdr->msgh_local_port = port_to_listen_on;
73 recv_hdr->msgh_size = sizeof(recv_msg);
74
75 kern_return_t kr =
76 mach_msg(recv_hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
77 recv_hdr->msgh_size, port_to_listen_on, 0, MACH_PORT_NULL);
78 if (kr != KERN_SUCCESS)
79 return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
80 if (recv_msg.header.msgh_id != 0)
81 return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
82 return base::mac::ScopedMachSendRight(recv_msg.data.name);
83 }
84
85 mach_port_name_t CreateIntermediateMachPort(
86 mach_port_t task_port,
87 base::mac::ScopedMachSendRight port_to_insert) {
88 DCHECK_NE(mach_task_self(), task_port);
89 DCHECK_NE(static_cast<mach_port_name_t>(MACH_PORT_NULL), task_port);
90
91 // Make a port with receive rights in the destination task.
92 mach_port_name_t endpoint;
93 kern_return_t kr =
94 mach_port_allocate(task_port, MACH_PORT_RIGHT_RECEIVE, &endpoint);
95 if (kr != KERN_SUCCESS) {
96 LOG(ERROR) << "Unable to allocate port with receive right";
97 return MACH_PORT_NULL;
98 }
99
100 // Change its message queue limit so that it accepts one message.
101 mach_port_limits limits = {};
102 limits.mpl_qlimit = 1;
103 kr = mach_port_set_attributes(task_port, endpoint, MACH_PORT_LIMITS_INFO,
104 reinterpret_cast<mach_port_info_t>(&limits),
105 MACH_PORT_LIMITS_INFO_COUNT);
106 if (kr != KERN_SUCCESS) {
107 LOG(ERROR) << "Unable to set port attributes";
108 mach_port_deallocate(task_port, endpoint);
109 return MACH_PORT_NULL;
110 }
111
112 // Get a send right.
113 mach_port_t send_once_right;
114 mach_msg_type_name_t send_right_type;
115 kr =
116 mach_port_extract_right(task_port, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
117 &send_once_right, &send_right_type);
118 if (kr != KERN_SUCCESS) {
119 LOG(ERROR) << "Unable to extra port send right";
120 mach_port_deallocate(task_port, endpoint);
121 return MACH_PORT_NULL;
122 }
123 DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE),
124 send_right_type);
125
126 // This call takes ownership of |send_once_right|.
127 kr = SendMachPort(
128 send_once_right, port_to_insert.get(), MACH_MSG_TYPE_COPY_SEND);
129 if (kr != KERN_SUCCESS) {
130 LOG(ERROR) << "Unable to send mach port";
131 mach_port_deallocate(task_port, endpoint);
132 return MACH_PORT_NULL;
133 }
134
135 // Endpoint is intentionally leaked into the destination task. An IPC must be
136 // sent to the destination task so that it can clean up this port.
137 return endpoint;
138 }
139
140 } // namespace
141
142 // static
143 bool MachPortRelay::ReceivePorts(PlatformHandleVector* handles) {
144 DCHECK(handles);
145
146 size_t num_received = 0;
147 for (size_t i = 0; i < handles->size(); i++) {
148 PlatformHandle* handle = handles->data() + i;
149 if (handle->type != PlatformHandle::Type::MACH)
150 continue;
151
152 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.
153 base::mac::ScopedMachSendRight received_port(
154 ReceiveMachPort(message_port.get()));
155 if (received_port.get() == MACH_PORT_NULL) {
156 LOG(ERROR) << "Error receiving mach port";
157 return false;
erikchen 2016/03/09 19:13:17 clear handle->port?
Anand Mistry (off Chromium) 2016/03/11 07:44:18 Done.
158 }
159
160 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.
161 handle->port = received_port.release();
162 }
163
164 return true;
165 }
166
167 MachPortRelay::MachPortRelay(base::PortProvider* port_provider)
168 : port_provider_(port_provider) {
169 DCHECK(port_provider);
170 port_provider_->AddObserver(this);
171 }
172
173 MachPortRelay::~MachPortRelay() {
174 port_provider_->RemoveObserver(this);
175 }
176
177 bool MachPortRelay::SendPortsToProcess(Channel::Message* message,
178 base::ProcessHandle process) {
179 DCHECK(message);
180 mach_port_t task_port = port_provider_->TaskForPid(process);
181 if (task_port == MACH_PORT_NULL)
182 return false;
183
184 size_t num_sent = 0;
185 bool error = false;
186 ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
187 // Message should have handles, otherwise there's no point in calling this
188 // function.
189 DCHECK(handles);
190 for (size_t i = 0; i < handles->size(); i++) {
191 PlatformHandle* handle = &(*handles)[i];
192 if (handle->type != PlatformHandle::Type::MACH)
193 continue;
194
195 mach_port_name_t intermediate_port;
196 DCHECK(handle->port != MACH_PORT_NULL);
197 intermediate_port = CreateIntermediateMachPort(
198 task_port, base::mac::ScopedMachSendRight(handle->port));
199 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.
200 error = true;
201 break;
202 }
203 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"
204 num_sent++;
205 }
206 DCHECK(error || num_sent);
207 message->SetHandles(std::move(handles));
208
209 return !error;
210 }
211
212 bool MachPortRelay::ExtractPortRights(Channel::Message* message,
213 base::ProcessHandle process) {
214 DCHECK(message);
215
216 mach_port_t task_port = port_provider_->TaskForPid(process);
217 if (task_port == MACH_PORT_NULL)
218 return false;
219
220 size_t num_received = 0;
221 bool error = false;
222 ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
223 // Message should have handles, otherwise there's no point in calling this
224 // function.
225 DCHECK(handles);
226 for (size_t i = 0; i < handles->size(); i++) {
227 PlatformHandle* handle = handles->data() + i;
228 if (handle->type != PlatformHandle::Type::MACH)
229 continue;
230
231 mach_port_t extracted_right = MACH_PORT_NULL;
232 mach_msg_type_name_t extracted_right_type;
233 kern_return_t kr =
234 mach_port_extract_right(task_port, handle->port,
235 MACH_MSG_TYPE_MOVE_SEND,
236 &extracted_right, &extracted_right_type);
237 if (kr != KERN_SUCCESS) {
238 error = true;
239 break;
240 }
241
242 DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
243 extracted_right_type);
244 handle->port = extracted_right;
245 num_received++;
246 }
247 DCHECK(error || num_received);
248 message->SetHandles(std::move(handles));
249
250 return !error;
251 }
252
253 void MachPortRelay::AddObserver(Observer* observer) {
254 base::AutoLock locker(observers_lock_);
255 bool inserted = observers_.insert(observer).second;
256 DCHECK(inserted);
257 }
258
259 void MachPortRelay::RemoveObserver(Observer* observer) {
260 base::AutoLock locker(observers_lock_);
261 observers_.erase(observer);
262 }
263
264 void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) {
265 base::AutoLock locker(observers_lock_);
266 for (const auto observer : observers_)
267 observer->OnProcessReady(process);
268 }
269
270 } // namespace edk
271 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698