| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/child_broker.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "mojo/edk/embedder/embedder_internal.h" | |
| 15 #include "mojo/edk/embedder/platform_channel_pair.h" | |
| 16 #include "mojo/edk/system/broker_messages.h" | |
| 17 #include "mojo/edk/system/message_pipe_dispatcher.h" | |
| 18 | |
| 19 namespace mojo { | |
| 20 namespace edk { | |
| 21 | |
| 22 ChildBroker* ChildBroker::GetInstance() { | |
| 23 return base::Singleton< | |
| 24 ChildBroker, base::LeakySingletonTraits<ChildBroker>>::get(); | |
| 25 } | |
| 26 | |
| 27 void ChildBroker::SetChildBrokerHostHandle(ScopedPlatformHandle handle) { | |
| 28 ScopedPlatformHandle parent_async_channel_handle; | |
| 29 #if defined(OS_POSIX) | |
| 30 parent_async_channel_handle = std::move(handle); | |
| 31 #else | |
| 32 // On Windows we have two pipes to the parent. The first is for the token | |
| 33 // exchange for creating and passing handles, since the child needs the | |
| 34 // parent's help if it is sandboxed. The second is the same as POSIX, which is | |
| 35 // used for multiplexing related messages. So on Windows, we send the second | |
| 36 // pipe as the first string over the first one. | |
| 37 parent_sync_channel_ = std::move(handle); | |
| 38 | |
| 39 HANDLE parent_handle = INVALID_HANDLE_VALUE; | |
| 40 DWORD bytes_read = 0; | |
| 41 BOOL rv = ReadFile(parent_sync_channel_.get().handle, &parent_handle, | |
| 42 sizeof(parent_handle), &bytes_read, NULL); | |
| 43 CHECK(rv); | |
| 44 parent_async_channel_handle.reset(PlatformHandle(parent_handle)); | |
| 45 sync_channel_lock_.Unlock(); | |
| 46 #endif | |
| 47 | |
| 48 internal::g_io_thread_task_runner->PostTask( | |
| 49 FROM_HERE, | |
| 50 base::Bind(&ChildBroker::InitAsyncChannel, base::Unretained(this), | |
| 51 base::Passed(&parent_async_channel_handle))); | |
| 52 } | |
| 53 | |
| 54 #if defined(OS_WIN) | |
| 55 void ChildBroker::CreatePlatformChannelPair( | |
| 56 ScopedPlatformHandle* server, ScopedPlatformHandle* client) { | |
| 57 sync_channel_lock_.Lock(); | |
| 58 CreatePlatformChannelPairNoLock(server, client); | |
| 59 sync_channel_lock_.Unlock(); | |
| 60 } | |
| 61 | |
| 62 void ChildBroker::HandleToToken(const PlatformHandle* platform_handles, | |
| 63 size_t count, | |
| 64 uint64_t* tokens) { | |
| 65 uint32_t size = kBrokerMessageHeaderSize + | |
| 66 static_cast<int>(count) * sizeof(HANDLE); | |
| 67 std::vector<char> message_buffer(size); | |
| 68 BrokerMessage* message = reinterpret_cast<BrokerMessage*>(&message_buffer[0]); | |
| 69 message->size = size; | |
| 70 message->id = HANDLE_TO_TOKEN; | |
| 71 for (size_t i = 0; i < count; ++i) | |
| 72 message->handles[i] = platform_handles[i].handle; | |
| 73 | |
| 74 uint32_t response_size = static_cast<int>(count) * sizeof(uint64_t); | |
| 75 sync_channel_lock_.Lock(); | |
| 76 WriteAndReadResponse(message, tokens, response_size); | |
| 77 sync_channel_lock_.Unlock(); | |
| 78 } | |
| 79 | |
| 80 void ChildBroker::TokenToHandle(const uint64_t* tokens, | |
| 81 size_t count, | |
| 82 PlatformHandle* handles) { | |
| 83 uint32_t size = kBrokerMessageHeaderSize + | |
| 84 static_cast<int>(count) * sizeof(uint64_t); | |
| 85 std::vector<char> message_buffer(size); | |
| 86 BrokerMessage* message = | |
| 87 reinterpret_cast<BrokerMessage*>(&message_buffer[0]); | |
| 88 message->size = size; | |
| 89 message->id = TOKEN_TO_HANDLE; | |
| 90 memcpy(&message->tokens[0], tokens, count * sizeof(uint64_t)); | |
| 91 | |
| 92 std::vector<HANDLE> handles_temp(count); | |
| 93 uint32_t response_size = | |
| 94 static_cast<uint32_t>(handles_temp.size()) * sizeof(HANDLE); | |
| 95 sync_channel_lock_.Lock(); | |
| 96 if (WriteAndReadResponse(message, &handles_temp[0], response_size)) { | |
| 97 for (uint32_t i = 0; i < count; ++i) | |
| 98 handles[i].handle = handles_temp[i]; | |
| 99 sync_channel_lock_.Unlock(); | |
| 100 } | |
| 101 } | |
| 102 #endif | |
| 103 | |
| 104 void ChildBroker::ConnectMessagePipe(uint64_t pipe_id, | |
| 105 MessagePipeDispatcher* message_pipe) { | |
| 106 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 107 | |
| 108 ConnectMessagePipeMessage data; | |
| 109 memset(&data, 0, sizeof(data)); | |
| 110 data.pipe_id = pipe_id; | |
| 111 if (pending_connects_.find(pipe_id) != pending_connects_.end()) { | |
| 112 if (!parent_async_channel_) { | |
| 113 // On Windows, we can't create the local RoutedRawChannel yet because we | |
| 114 // don't have parent_sync_channel_. Treat all platforms the same and just | |
| 115 // queue this. | |
| 116 CHECK(pending_inprocess_connects_.find(pipe_id) == | |
| 117 pending_inprocess_connects_.end()); | |
| 118 pending_inprocess_connects_[pipe_id] = message_pipe; | |
| 119 return; | |
| 120 } | |
| 121 // Both ends of the message pipe are in the same process. | |
| 122 // First, tell the browser side that to remove its bookkeeping for a pending | |
| 123 // connect, since it'll never get the other side. | |
| 124 | |
| 125 data.type = CANCEL_CONNECT_MESSAGE_PIPE; | |
| 126 scoped_ptr<MessageInTransit> message(new MessageInTransit( | |
| 127 MessageInTransit::Type::MESSAGE, sizeof(data), &data)); | |
| 128 WriteAsyncMessage(std::move(message)); | |
| 129 | |
| 130 if (!in_process_pipes_channel1_) { | |
| 131 ScopedPlatformHandle server_handle, client_handle; | |
| 132 #if defined(OS_WIN) | |
| 133 CreatePlatformChannelPairNoLock(&server_handle, &client_handle); | |
| 134 #else | |
| 135 PlatformChannelPair channel_pair; | |
| 136 server_handle = channel_pair.PassServerHandle(); | |
| 137 client_handle = channel_pair.PassClientHandle(); | |
| 138 #endif | |
| 139 in_process_pipes_channel1_ = new RoutedRawChannel( | |
| 140 std::move(server_handle), | |
| 141 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
| 142 in_process_pipes_channel2_ = new RoutedRawChannel( | |
| 143 std::move(client_handle), | |
| 144 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
| 145 } | |
| 146 | |
| 147 AttachMessagePipe(pending_connects_[pipe_id], pipe_id, | |
| 148 in_process_pipes_channel1_); | |
| 149 AttachMessagePipe(message_pipe, pipe_id, in_process_pipes_channel2_); | |
| 150 pending_connects_.erase(pipe_id); | |
| 151 return; | |
| 152 } | |
| 153 | |
| 154 data.type = CONNECT_MESSAGE_PIPE; | |
| 155 scoped_ptr<MessageInTransit> message(new MessageInTransit( | |
| 156 MessageInTransit::Type::MESSAGE, sizeof(data), &data)); | |
| 157 pending_connects_[pipe_id] = message_pipe; | |
| 158 WriteAsyncMessage(std::move(message)); | |
| 159 } | |
| 160 | |
| 161 void ChildBroker::CloseMessagePipe( | |
| 162 uint64_t pipe_id, MessagePipeDispatcher* message_pipe) { | |
| 163 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 164 CHECK(connected_pipes_.find(message_pipe) != connected_pipes_.end()); | |
| 165 connected_pipes_[message_pipe]->RemoveRoute(pipe_id); | |
| 166 connected_pipes_.erase(message_pipe); | |
| 167 } | |
| 168 | |
| 169 ChildBroker::ChildBroker() | |
| 170 : parent_async_channel_(nullptr), | |
| 171 in_process_pipes_channel1_(nullptr), | |
| 172 in_process_pipes_channel2_(nullptr) { | |
| 173 DCHECK(!internal::g_broker); | |
| 174 internal::g_broker = this; | |
| 175 #if defined(OS_WIN) | |
| 176 // Block any threads from calling this until we have a pipe to the parent. | |
| 177 sync_channel_lock_.Lock(); | |
| 178 #endif | |
| 179 } | |
| 180 | |
| 181 ChildBroker::~ChildBroker() { | |
| 182 } | |
| 183 | |
| 184 void ChildBroker::OnReadMessage( | |
| 185 const MessageInTransit::View& message_view, | |
| 186 ScopedPlatformHandleVectorPtr platform_handles) { | |
| 187 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 188 MultiplexMessages type = | |
| 189 *static_cast<const MultiplexMessages*>(message_view.bytes()); | |
| 190 if (type == CONNECT_TO_PROCESS) { | |
| 191 DCHECK_EQ(platform_handles->size(), 1u); | |
| 192 ScopedPlatformHandle handle((*platform_handles.get())[0]); | |
| 193 (*platform_handles.get())[0] = PlatformHandle(); | |
| 194 | |
| 195 const ConnectToProcessMessage* message = | |
| 196 static_cast<const ConnectToProcessMessage*>(message_view.bytes()); | |
| 197 | |
| 198 CHECK(channels_.find(message->process_id) == channels_.end()); | |
| 199 channels_[message->process_id] = new RoutedRawChannel( | |
| 200 std::move(handle), | |
| 201 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
| 202 } else if (type == PEER_PIPE_CONNECTED) { | |
| 203 DCHECK(!platform_handles); | |
| 204 const PeerPipeConnectedMessage* message = | |
| 205 static_cast<const PeerPipeConnectedMessage*>(message_view.bytes()); | |
| 206 | |
| 207 uint64_t pipe_id = message->pipe_id; | |
| 208 uint64_t peer_pid = message->process_id; | |
| 209 | |
| 210 CHECK(pending_connects_.find(pipe_id) != pending_connects_.end()); | |
| 211 MessagePipeDispatcher* pipe = pending_connects_[pipe_id]; | |
| 212 pending_connects_.erase(pipe_id); | |
| 213 if (peer_pid == 0) { | |
| 214 // The other side is in the parent process. | |
| 215 AttachMessagePipe(pipe, pipe_id, parent_async_channel_); | |
| 216 } else if (channels_.find(peer_pid) == channels_.end()) { | |
| 217 // We saw the peer process die before we got the reply from the parent. | |
| 218 pipe->OnError(ERROR_READ_SHUTDOWN); | |
| 219 } else { | |
| 220 CHECK(connected_pipes_.find(pipe) == connected_pipes_.end()); | |
| 221 AttachMessagePipe(pipe, pipe_id, channels_[peer_pid]); | |
| 222 } | |
| 223 } else if (type == PEER_DIED) { | |
| 224 DCHECK(!platform_handles); | |
| 225 const PeerDiedMessage* message = | |
| 226 static_cast<const PeerDiedMessage*>(message_view.bytes()); | |
| 227 | |
| 228 uint64_t pipe_id = message->pipe_id; | |
| 229 | |
| 230 CHECK(pending_connects_.find(pipe_id) != pending_connects_.end()); | |
| 231 MessagePipeDispatcher* pipe = pending_connects_[pipe_id]; | |
| 232 pending_connects_.erase(pipe_id); | |
| 233 pipe->OnError(ERROR_READ_SHUTDOWN); | |
| 234 } else { | |
| 235 NOTREACHED(); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void ChildBroker::OnError(Error error) { | |
| 240 // The parent process shut down. | |
| 241 } | |
| 242 | |
| 243 void ChildBroker::ChannelDestructed(RoutedRawChannel* channel) { | |
| 244 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 245 for (auto it : channels_) { | |
| 246 if (it.second == channel) { | |
| 247 channels_.erase(it.first); | |
| 248 break; | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void ChildBroker::WriteAsyncMessage(scoped_ptr<MessageInTransit> message) { | |
| 254 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 255 message->set_route_id(kBrokerRouteId); | |
| 256 if (parent_async_channel_) { | |
| 257 parent_async_channel_->channel()->WriteMessage(std::move(message)); | |
| 258 } else { | |
| 259 async_channel_queue_.AddMessage(std::move(message)); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 void ChildBroker::InitAsyncChannel( | |
| 264 ScopedPlatformHandle parent_async_channel_handle) { | |
| 265 DCHECK(internal::g_io_thread_task_runner->RunsTasksOnCurrentThread()); | |
| 266 | |
| 267 parent_async_channel_ = new RoutedRawChannel( | |
| 268 std::move(parent_async_channel_handle), | |
| 269 base::Bind(&ChildBroker::ChannelDestructed, base::Unretained(this))); | |
| 270 parent_async_channel_->AddRoute(kBrokerRouteId, this); | |
| 271 while (!async_channel_queue_.IsEmpty()) { | |
| 272 parent_async_channel_->channel()->WriteMessage( | |
| 273 async_channel_queue_.GetMessage()); | |
| 274 } | |
| 275 | |
| 276 while (!pending_inprocess_connects_.empty()) { | |
| 277 ConnectMessagePipe(pending_inprocess_connects_.begin()->first, | |
| 278 pending_inprocess_connects_.begin()->second); | |
| 279 pending_inprocess_connects_.erase(pending_inprocess_connects_.begin()); | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 void ChildBroker::AttachMessagePipe(MessagePipeDispatcher* message_pipe, | |
| 284 uint64_t pipe_id, | |
| 285 RoutedRawChannel* raw_channel) { | |
| 286 connected_pipes_[message_pipe] = raw_channel; | |
| 287 // Note: we must call GotNonTransferableChannel before AddRoute because there | |
| 288 // could be race conditions if the pipe got queued messages in |AddRoute| but | |
| 289 // then when it's read it returns no messages because it doesn't have the | |
| 290 // channel yet. | |
| 291 message_pipe->GotNonTransferableChannel(raw_channel->channel()); | |
| 292 // The above call could have caused |CloseMessagePipe| to be called. | |
| 293 if (connected_pipes_.find(message_pipe) != connected_pipes_.end()) | |
| 294 raw_channel->AddRoute(pipe_id, message_pipe); | |
| 295 } | |
| 296 | |
| 297 #if defined(OS_WIN) | |
| 298 | |
| 299 bool ChildBroker::WriteAndReadResponse(BrokerMessage* message, | |
| 300 void* response, | |
| 301 uint32_t response_size) { | |
| 302 CHECK(parent_sync_channel_.is_valid()); | |
| 303 | |
| 304 bool result = true; | |
| 305 DWORD bytes_written = 0; | |
| 306 // This will always write in one chunk per | |
| 307 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150.aspx. | |
| 308 BOOL rv = WriteFile(parent_sync_channel_.get().handle, message, message->size, | |
| 309 &bytes_written, NULL); | |
| 310 if (!rv || bytes_written != message->size) { | |
| 311 LOG(ERROR) << "Child token serializer couldn't write message."; | |
| 312 result = false; | |
| 313 } else { | |
| 314 while (response_size) { | |
| 315 DWORD bytes_read = 0; | |
| 316 rv = ReadFile(parent_sync_channel_.get().handle, response, response_size, | |
| 317 &bytes_read, NULL); | |
| 318 if (!rv) { | |
| 319 LOG(ERROR) << "Child token serializer couldn't read result."; | |
| 320 result = false; | |
| 321 break; | |
| 322 } | |
| 323 response_size -= bytes_read; | |
| 324 response = static_cast<char*>(response) + bytes_read; | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 return result; | |
| 329 } | |
| 330 | |
| 331 void ChildBroker::CreatePlatformChannelPairNoLock( | |
| 332 ScopedPlatformHandle* server, ScopedPlatformHandle* client) { | |
| 333 BrokerMessage message; | |
| 334 message.size = kBrokerMessageHeaderSize; | |
| 335 message.id = CREATE_PLATFORM_CHANNEL_PAIR; | |
| 336 | |
| 337 uint32_t response_size = 2 * sizeof(HANDLE); | |
| 338 HANDLE handles[2]; | |
| 339 if (WriteAndReadResponse(&message, handles, response_size)) { | |
| 340 server->reset(PlatformHandle(handles[0])); | |
| 341 client->reset(PlatformHandle(handles[1])); | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 #endif | |
| 346 | |
| 347 } // namespace edk | |
| 348 } // namespace mojo | |
| OLD | NEW |