// Copyright 2013 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/core.h" #include <string.h> #include <utility> #include "base/bind.h" #include "base/containers/stack_container.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/rand_util.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/embedder_internal.h" #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/system/channel.h" #include "mojo/edk/system/configuration.h" #include "mojo/edk/system/data_pipe_consumer_dispatcher.h" #include "mojo/edk/system/data_pipe_producer_dispatcher.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/message_for_transit.h" #include "mojo/edk/system/message_pipe_dispatcher.h" #include "mojo/edk/system/platform_handle_dispatcher.h" #include "mojo/edk/system/ports/name.h" #include "mojo/edk/system/ports/node.h" #include "mojo/edk/system/request_context.h" #include "mojo/edk/system/shared_buffer_dispatcher.h" #include "mojo/edk/system/wait_set_dispatcher.h" #include "mojo/edk/system/waiter.h" namespace mojo { namespace edk { namespace { // This is an unnecessarily large limit that is relatively easy to enforce. const uint32_t kMaxHandlesPerMessage = 1024 * 1024; // TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process // pipes too; for now we just use a constant. This only affects bootstrap pipes. const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL; void CallWatchCallback(MojoWatchCallback callback, uintptr_t context, MojoResult result, const HandleSignalsState& signals_state, MojoWatchNotificationFlags flags) { callback(context, result, static_cast<MojoHandleSignalsState>(signals_state), flags); } MojoResult MojoPlatformHandleToScopedPlatformHandle( const MojoPlatformHandle* platform_handle, ScopedPlatformHandle* out_handle) { if (platform_handle->struct_size != sizeof(MojoPlatformHandle)) return MOJO_RESULT_INVALID_ARGUMENT; if (platform_handle->type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { out_handle->reset(); return MOJO_RESULT_OK; } PlatformHandle handle; switch (platform_handle->type) { #if defined(OS_POSIX) case MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR: handle.handle = static_cast<int>(platform_handle->value); break; #endif #if defined(OS_MACOSX) && !defined(OS_IOS) case MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT: handle.type = PlatformHandle::Type::MACH; handle.port = static_cast<mach_port_t>(platform_handle->value); break; #endif #if defined(OS_WIN) case MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE: handle.handle = reinterpret_cast<HANDLE>(platform_handle->value); break; #endif default: return MOJO_RESULT_INVALID_ARGUMENT; } out_handle->reset(handle); return MOJO_RESULT_OK; } MojoResult ScopedPlatformHandleToMojoPlatformHandle( ScopedPlatformHandle handle, MojoPlatformHandle* platform_handle) { if (platform_handle->struct_size != sizeof(MojoPlatformHandle)) return MOJO_RESULT_INVALID_ARGUMENT; if (!handle.is_valid()) { platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_INVALID; return MOJO_RESULT_OK; } #if defined(OS_POSIX) switch (handle.get().type) { case PlatformHandle::Type::POSIX: platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; platform_handle->value = static_cast<uint64_t>(handle.release().handle); break; #if defined(OS_MACOSX) && !defined(OS_IOS) case PlatformHandle::Type::MACH: platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; platform_handle->value = static_cast<uint64_t>(handle.release().port); break; #endif // defined(OS_MACOSX) && !defined(OS_IOS) default: return MOJO_RESULT_INVALID_ARGUMENT; } #elif defined(OS_WIN) platform_handle->type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; platform_handle->value = reinterpret_cast<uint64_t>(handle.release().handle); #endif // defined(OS_WIN) return MOJO_RESULT_OK; } } // namespace Core::Core() {} Core::~Core() { if (node_controller_ && node_controller_->io_task_runner()) { // If this races with IO thread shutdown the callback will be dropped and // the NodeController will be shutdown on this thread anyway, which is also // just fine. scoped_refptr<base::TaskRunner> io_task_runner = node_controller_->io_task_runner(); io_task_runner->PostTask(FROM_HERE, base::Bind(&Core::PassNodeControllerToIOThread, base::Passed(&node_controller_))); } } void Core::SetIOTaskRunner(scoped_refptr<base::TaskRunner> io_task_runner) { GetNodeController()->SetIOTaskRunner(io_task_runner); } NodeController* Core::GetNodeController() { base::AutoLock lock(node_controller_lock_); if (!node_controller_) node_controller_.reset(new NodeController(this)); return node_controller_.get(); } scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { base::AutoLock lock(handles_lock_); return handles_.GetDispatcher(handle); } void Core::SetDefaultProcessErrorCallback( const ProcessErrorCallback& callback) { default_process_error_callback_ = callback; } void Core::AddChild(base::ProcessHandle process_handle, ConnectionParams connection_params, const std::string& child_token, const ProcessErrorCallback& process_error_callback) { GetNodeController()->ConnectToChild(process_handle, std::move(connection_params), child_token, process_error_callback); } void Core::ChildLaunchFailed(const std::string& child_token) { RequestContext request_context; GetNodeController()->CloseChildPorts(child_token); } ScopedMessagePipeHandle Core::ConnectToPeerProcess( ScopedPlatformHandle pipe_handle, const std::string& peer_token) { RequestContext request_context; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); MojoHandle handle = AddDispatcher(new MessagePipeDispatcher( GetNodeController(), port0, kUnknownPipeIdForDebug, 0)); ConnectionParams connection_params(std::move(pipe_handle)); GetNodeController()->ConnectToPeer(std::move(connection_params), port1, peer_token); return ScopedMessagePipeHandle(MessagePipeHandle(handle)); } void Core::ClosePeerConnection(const std::string& peer_token) { GetNodeController()->ClosePeerConnection(peer_token); } void Core::InitChild(ConnectionParams connection_params) { GetNodeController()->ConnectToParent(std::move(connection_params)); } void Core::SetMachPortProvider(base::PortProvider* port_provider) { #if defined(OS_MACOSX) && !defined(OS_IOS) GetNodeController()->CreateMachPortRelay(port_provider); #endif } MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { base::AutoLock lock(handles_lock_); return handles_.AddDispatcher(dispatcher); } bool Core::AddDispatchersFromTransit( const std::vector<Dispatcher::DispatcherInTransit>& dispatchers, MojoHandle* handles) { bool failed = false; { base::AutoLock lock(handles_lock_); if (!handles_.AddDispatchersFromTransit(dispatchers, handles)) failed = true; } if (failed) { for (auto d : dispatchers) d.dispatcher->Close(); return false; } return true; } MojoResult Core::CreatePlatformHandleWrapper( ScopedPlatformHandle platform_handle, MojoHandle* wrapper_handle) { MojoHandle h = AddDispatcher( PlatformHandleDispatcher::Create(std::move(platform_handle))); if (h == MOJO_HANDLE_INVALID) return MOJO_RESULT_RESOURCE_EXHAUSTED; *wrapper_handle = h; return MOJO_RESULT_OK; } MojoResult Core::PassWrappedPlatformHandle( MojoHandle wrapper_handle, ScopedPlatformHandle* platform_handle) { base::AutoLock lock(handles_lock_); scoped_refptr<Dispatcher> d; MojoResult result = handles_.GetAndRemoveDispatcher(wrapper_handle, &d); if (result != MOJO_RESULT_OK) return result; if (d->GetType() == Dispatcher::Type::PLATFORM_HANDLE) { PlatformHandleDispatcher* phd = static_cast<PlatformHandleDispatcher*>(d.get()); *platform_handle = phd->PassPlatformHandle(); } else { result = MOJO_RESULT_INVALID_ARGUMENT; } d->Close(); return result; } MojoResult Core::CreateSharedBufferWrapper( base::SharedMemoryHandle shared_memory_handle, size_t num_bytes, bool read_only, MojoHandle* mojo_wrapper_handle) { DCHECK(num_bytes); scoped_refptr<PlatformSharedBuffer> platform_buffer = PlatformSharedBuffer::CreateFromSharedMemoryHandle(num_bytes, read_only, shared_memory_handle); if (!platform_buffer) return MOJO_RESULT_UNKNOWN; scoped_refptr<SharedBufferDispatcher> dispatcher; MojoResult result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer( platform_buffer, &dispatcher); if (result != MOJO_RESULT_OK) return result; MojoHandle h = AddDispatcher(dispatcher); if (h == MOJO_HANDLE_INVALID) return MOJO_RESULT_RESOURCE_EXHAUSTED; *mojo_wrapper_handle = h; return MOJO_RESULT_OK; } MojoResult Core::PassSharedMemoryHandle( MojoHandle mojo_handle, base::SharedMemoryHandle* shared_memory_handle, size_t* num_bytes, bool* read_only) { if (!shared_memory_handle) return MOJO_RESULT_INVALID_ARGUMENT; scoped_refptr<Dispatcher> dispatcher; MojoResult result = MOJO_RESULT_OK; { base::AutoLock lock(handles_lock_); // Get the dispatcher and check it before removing it from the handle table // to ensure that the dispatcher is of the correct type. This ensures we // don't close and remove the wrong type of dispatcher. dispatcher = handles_.GetDispatcher(mojo_handle); if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) return MOJO_RESULT_INVALID_ARGUMENT; result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher); if (result != MOJO_RESULT_OK) return result; } SharedBufferDispatcher* shm_dispatcher = static_cast<SharedBufferDispatcher*>(dispatcher.get()); scoped_refptr<PlatformSharedBuffer> platform_shared_buffer = shm_dispatcher->PassPlatformSharedBuffer(); if (!platform_shared_buffer) return MOJO_RESULT_INVALID_ARGUMENT; if (num_bytes) *num_bytes = platform_shared_buffer->GetNumBytes(); if (read_only) *read_only = platform_shared_buffer->IsReadOnly(); *shared_memory_handle = platform_shared_buffer->DuplicateSharedMemoryHandle(); shm_dispatcher->Close(); return result; } void Core::RequestShutdown(const base::Closure& callback) { GetNodeController()->RequestShutdown(callback); } ScopedMessagePipeHandle Core::CreateParentMessagePipe( const std::string& token, const std::string& child_token) { RequestContext request_context; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); MojoHandle handle = AddDispatcher( new MessagePipeDispatcher(GetNodeController(), port0, kUnknownPipeIdForDebug, 0)); GetNodeController()->ReservePort(token, port1, child_token); return ScopedMessagePipeHandle(MessagePipeHandle(handle)); } ScopedMessagePipeHandle Core::CreateChildMessagePipe(const std::string& token) { RequestContext request_context; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); MojoHandle handle = AddDispatcher( new MessagePipeDispatcher(GetNodeController(), port0, kUnknownPipeIdForDebug, 1)); GetNodeController()->MergePortIntoParent(token, port1); return ScopedMessagePipeHandle(MessagePipeHandle(handle)); } MojoResult Core::SetProperty(MojoPropertyType type, const void* value) { base::AutoLock locker(property_lock_); switch (type) { case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED: property_sync_call_allowed_ = *static_cast<const bool*>(value); return MOJO_RESULT_OK; default: return MOJO_RESULT_INVALID_ARGUMENT; } } MojoTimeTicks Core::GetTimeTicksNow() { return base::TimeTicks::Now().ToInternalValue(); } MojoResult Core::Close(MojoHandle handle) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher; { base::AutoLock lock(handles_lock_); MojoResult rv = handles_.GetAndRemoveDispatcher(handle, &dispatcher); if (rv != MOJO_RESULT_OK) return rv; } dispatcher->Close(); return MOJO_RESULT_OK; } MojoResult Core::Wait(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline, MojoHandleSignalsState* signals_state) { RequestContext request_context; uint32_t unused = static_cast<uint32_t>(-1); HandleSignalsState hss; MojoResult rv = WaitManyInternal(&handle, &signals, 1, deadline, &unused, signals_state ? &hss : nullptr); if (rv != MOJO_RESULT_INVALID_ARGUMENT && signals_state) *signals_state = hss; return rv; } MojoResult Core::WaitMany(const MojoHandle* handles, const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, uint32_t* result_index, MojoHandleSignalsState* signals_state) { RequestContext request_context; if (num_handles < 1) return MOJO_RESULT_INVALID_ARGUMENT; if (num_handles > GetConfiguration().max_wait_many_num_handles) return MOJO_RESULT_RESOURCE_EXHAUSTED; uint32_t index = static_cast<uint32_t>(-1); MojoResult rv; if (!signals_state) { rv = WaitManyInternal(handles, signals, num_handles, deadline, &index, nullptr); } else { // Note: The |reinterpret_cast| is safe, since |HandleSignalsState| is a // subclass of |MojoHandleSignalsState| that doesn't add any data members. rv = WaitManyInternal(handles, signals, num_handles, deadline, &index, reinterpret_cast<HandleSignalsState*>(signals_state)); } if (index != static_cast<uint32_t>(-1) && result_index) *result_index = index; return rv; } MojoResult Core::Watch(MojoHandle handle, MojoHandleSignals signals, MojoWatchCallback callback, uintptr_t context) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->Watch( signals, base::Bind(&CallWatchCallback, callback, context), context); } MojoResult Core::CancelWatch(MojoHandle handle, uintptr_t context) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->CancelWatch(context); } MojoResult Core::AllocMessage(uint32_t num_bytes, const MojoHandle* handles, uint32_t num_handles, MojoAllocMessageFlags flags, MojoMessageHandle* message) { if (!message) return MOJO_RESULT_INVALID_ARGUMENT; if (num_handles == 0) { // Fast path: no handles. std::unique_ptr<MessageForTransit> msg; MojoResult rv = MessageForTransit::Create(&msg, num_bytes, nullptr, 0); if (rv != MOJO_RESULT_OK) return rv; *message = reinterpret_cast<MojoMessageHandle>(msg.release()); return MOJO_RESULT_OK; } if (!handles) return MOJO_RESULT_INVALID_ARGUMENT; if (num_handles > kMaxHandlesPerMessage) return MOJO_RESULT_RESOURCE_EXHAUSTED; std::vector<Dispatcher::DispatcherInTransit> dispatchers; { base::AutoLock lock(handles_lock_); MojoResult rv = handles_.BeginTransit(handles, num_handles, &dispatchers); if (rv != MOJO_RESULT_OK) { handles_.CancelTransit(dispatchers); return rv; } } DCHECK_EQ(num_handles, dispatchers.size()); std::unique_ptr<MessageForTransit> msg; MojoResult rv = MessageForTransit::Create( &msg, num_bytes, dispatchers.data(), num_handles); { base::AutoLock lock(handles_lock_); if (rv == MOJO_RESULT_OK) { handles_.CompleteTransitAndClose(dispatchers); *message = reinterpret_cast<MojoMessageHandle>(msg.release()); } else { handles_.CancelTransit(dispatchers); } } return rv; } MojoResult Core::FreeMessage(MojoMessageHandle message) { if (!message) return MOJO_RESULT_INVALID_ARGUMENT; delete reinterpret_cast<MessageForTransit*>(message); return MOJO_RESULT_OK; } MojoResult Core::GetMessageBuffer(MojoMessageHandle message, void** buffer) { if (!message) return MOJO_RESULT_INVALID_ARGUMENT; *buffer = reinterpret_cast<MessageForTransit*>(message)->mutable_bytes(); return MOJO_RESULT_OK; } MojoResult Core::GetProperty(MojoPropertyType type, void* value) { base::AutoLock locker(property_lock_); switch (type) { case MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED: *static_cast<bool*>(value) = property_sync_call_allowed_; return MOJO_RESULT_OK; default: return MOJO_RESULT_INVALID_ARGUMENT; } } MojoResult Core::CreateWaitSet(MojoHandle* wait_set_handle) { RequestContext request_context; if (!wait_set_handle) return MOJO_RESULT_INVALID_ARGUMENT; scoped_refptr<WaitSetDispatcher> dispatcher = new WaitSetDispatcher(); MojoHandle h = AddDispatcher(dispatcher); if (h == MOJO_HANDLE_INVALID) { LOG(ERROR) << "Handle table full"; dispatcher->Close(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } *wait_set_handle = h; return MOJO_RESULT_OK; } MojoResult Core::AddHandle(MojoHandle wait_set_handle, MojoHandle handle, MojoHandleSignals signals) { RequestContext request_context; scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle)); if (!wait_set_dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return wait_set_dispatcher->AddWaitingDispatcher(dispatcher, signals, handle); } MojoResult Core::RemoveHandle(MojoHandle wait_set_handle, MojoHandle handle) { RequestContext request_context; scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle)); if (!wait_set_dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; scoped_refptr<Dispatcher> dispatcher(GetDispatcher(handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return wait_set_dispatcher->RemoveWaitingDispatcher(dispatcher); } MojoResult Core::GetReadyHandles(MojoHandle wait_set_handle, uint32_t* count, MojoHandle* handles, MojoResult* results, MojoHandleSignalsState* signals_states) { RequestContext request_context; if (!handles || !count || !(*count) || !results) return MOJO_RESULT_INVALID_ARGUMENT; scoped_refptr<Dispatcher> wait_set_dispatcher(GetDispatcher(wait_set_handle)); if (!wait_set_dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; DispatcherVector awoken_dispatchers; base::StackVector<uintptr_t, 16> contexts; contexts->assign(*count, MOJO_HANDLE_INVALID); MojoResult result = wait_set_dispatcher->GetReadyDispatchers( count, &awoken_dispatchers, results, contexts->data()); if (result == MOJO_RESULT_OK) { for (size_t i = 0; i < *count; i++) { handles[i] = static_cast<MojoHandle>(contexts[i]); if (signals_states) signals_states[i] = awoken_dispatchers[i]->GetHandleSignalsState(); } } return result; } MojoResult Core::CreateMessagePipe( const MojoCreateMessagePipeOptions* options, MojoHandle* message_pipe_handle0, MojoHandle* message_pipe_handle1) { RequestContext request_context; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); CHECK(message_pipe_handle0); CHECK(message_pipe_handle1); uint64_t pipe_id = base::RandUint64(); *message_pipe_handle0 = AddDispatcher( new MessagePipeDispatcher(GetNodeController(), port0, pipe_id, 0)); if (*message_pipe_handle0 == MOJO_HANDLE_INVALID) return MOJO_RESULT_RESOURCE_EXHAUSTED; *message_pipe_handle1 = AddDispatcher( new MessagePipeDispatcher(GetNodeController(), port1, pipe_id, 1)); if (*message_pipe_handle1 == MOJO_HANDLE_INVALID) { scoped_refptr<Dispatcher> unused; unused->Close(); base::AutoLock lock(handles_lock_); handles_.GetAndRemoveDispatcher(*message_pipe_handle0, &unused); return MOJO_RESULT_RESOURCE_EXHAUSTED; } return MOJO_RESULT_OK; } MojoResult Core::WriteMessage(MojoHandle message_pipe_handle, const void* bytes, uint32_t num_bytes, const MojoHandle* handles, uint32_t num_handles, MojoWriteMessageFlags flags) { if (num_bytes && !bytes) return MOJO_RESULT_INVALID_ARGUMENT; MojoMessageHandle message; MojoResult rv = AllocMessage(num_bytes, handles, num_handles, MOJO_ALLOC_MESSAGE_FLAG_NONE, &message); if (rv != MOJO_RESULT_OK) return rv; if (num_bytes) { void* buffer = nullptr; rv = GetMessageBuffer(message, &buffer); DCHECK_EQ(rv, MOJO_RESULT_OK); memcpy(buffer, bytes, num_bytes); } return WriteMessageNew(message_pipe_handle, message, flags); } MojoResult Core::WriteMessageNew(MojoHandle message_pipe_handle, MojoMessageHandle message, MojoWriteMessageFlags flags) { RequestContext request_context; std::unique_ptr<MessageForTransit> message_for_transit( reinterpret_cast<MessageForTransit*>(message)); auto dispatcher = GetDispatcher(message_pipe_handle); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->WriteMessage(std::move(message_for_transit), flags); } MojoResult Core::ReadMessage(MojoHandle message_pipe_handle, void* bytes, uint32_t* num_bytes, MojoHandle* handles, uint32_t* num_handles, MojoReadMessageFlags flags) { CHECK((!num_handles || !*num_handles || handles) && (!num_bytes || !*num_bytes || bytes)); RequestContext request_context; auto dispatcher = GetDispatcher(message_pipe_handle); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; std::unique_ptr<MessageForTransit> message; MojoResult rv = dispatcher->ReadMessage(&message, num_bytes, handles, num_handles, flags, false /* ignore_num_bytes */); if (rv != MOJO_RESULT_OK) return rv; if (message && message->num_bytes()) memcpy(bytes, message->bytes(), message->num_bytes()); return MOJO_RESULT_OK; } MojoResult Core::ReadMessageNew(MojoHandle message_pipe_handle, MojoMessageHandle* message, uint32_t* num_bytes, MojoHandle* handles, uint32_t* num_handles, MojoReadMessageFlags flags) { CHECK(message); CHECK(!num_handles || !*num_handles || handles); RequestContext request_context; auto dispatcher = GetDispatcher(message_pipe_handle); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; std::unique_ptr<MessageForTransit> msg; MojoResult rv = dispatcher->ReadMessage(&msg, num_bytes, handles, num_handles, flags, true /* ignore_num_bytes */); if (rv != MOJO_RESULT_OK) return rv; *message = reinterpret_cast<MojoMessageHandle>(msg.release()); return MOJO_RESULT_OK; } MojoResult Core::FuseMessagePipes(MojoHandle handle0, MojoHandle handle1) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher0; scoped_refptr<Dispatcher> dispatcher1; bool valid_handles = true; { base::AutoLock lock(handles_lock_); MojoResult result0 = handles_.GetAndRemoveDispatcher(handle0, &dispatcher0); MojoResult result1 = handles_.GetAndRemoveDispatcher(handle1, &dispatcher1); if (result0 != MOJO_RESULT_OK || result1 != MOJO_RESULT_OK || dispatcher0->GetType() != Dispatcher::Type::MESSAGE_PIPE || dispatcher1->GetType() != Dispatcher::Type::MESSAGE_PIPE) valid_handles = false; } if (!valid_handles) { if (dispatcher0) dispatcher0->Close(); if (dispatcher1) dispatcher1->Close(); return MOJO_RESULT_INVALID_ARGUMENT; } MessagePipeDispatcher* mpd0 = static_cast<MessagePipeDispatcher*>(dispatcher0.get()); MessagePipeDispatcher* mpd1 = static_cast<MessagePipeDispatcher*>(dispatcher1.get()); if (!mpd0->Fuse(mpd1)) return MOJO_RESULT_FAILED_PRECONDITION; return MOJO_RESULT_OK; } MojoResult Core::NotifyBadMessage(MojoMessageHandle message, const char* error, size_t error_num_bytes) { if (!message) return MOJO_RESULT_INVALID_ARGUMENT; const PortsMessage& ports_message = reinterpret_cast<MessageForTransit*>(message)->ports_message(); if (ports_message.source_node() == ports::kInvalidNodeName) { DVLOG(1) << "Received invalid message from unknown node."; if (!default_process_error_callback_.is_null()) default_process_error_callback_.Run(std::string(error, error_num_bytes)); return MOJO_RESULT_OK; } GetNodeController()->NotifyBadMessageFrom( ports_message.source_node(), std::string(error, error_num_bytes)); return MOJO_RESULT_OK; } MojoResult Core::CreateDataPipe( const MojoCreateDataPipeOptions* options, MojoHandle* data_pipe_producer_handle, MojoHandle* data_pipe_consumer_handle) { RequestContext request_context; if (options && options->struct_size != sizeof(MojoCreateDataPipeOptions)) return MOJO_RESULT_INVALID_ARGUMENT; MojoCreateDataPipeOptions create_options; create_options.struct_size = sizeof(MojoCreateDataPipeOptions); create_options.flags = options ? options->flags : 0; create_options.element_num_bytes = options ? options->element_num_bytes : 1; // TODO(rockot): Use Configuration to get default data pipe capacity. create_options.capacity_num_bytes = options && options->capacity_num_bytes ? options->capacity_num_bytes : 64 * 1024; // TODO(rockot): Broker through the parent when necessary. scoped_refptr<PlatformSharedBuffer> ring_buffer = GetNodeController()->CreateSharedBuffer( create_options.capacity_num_bytes); if (!ring_buffer) return MOJO_RESULT_RESOURCE_EXHAUSTED; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); CHECK(data_pipe_producer_handle); CHECK(data_pipe_consumer_handle); uint64_t pipe_id = base::RandUint64(); scoped_refptr<Dispatcher> producer = new DataPipeProducerDispatcher( GetNodeController(), port0, ring_buffer, create_options, true /* initialized */, pipe_id); scoped_refptr<Dispatcher> consumer = new DataPipeConsumerDispatcher( GetNodeController(), port1, ring_buffer, create_options, true /* initialized */, pipe_id); *data_pipe_producer_handle = AddDispatcher(producer); *data_pipe_consumer_handle = AddDispatcher(consumer); if (*data_pipe_producer_handle == MOJO_HANDLE_INVALID || *data_pipe_consumer_handle == MOJO_HANDLE_INVALID) { if (*data_pipe_producer_handle != MOJO_HANDLE_INVALID) { scoped_refptr<Dispatcher> unused; base::AutoLock lock(handles_lock_); handles_.GetAndRemoveDispatcher(*data_pipe_producer_handle, &unused); } producer->Close(); consumer->Close(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } return MOJO_RESULT_OK; } MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle, const void* elements, uint32_t* num_bytes, MojoWriteDataFlags flags) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_producer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->WriteData(elements, num_bytes, flags); } MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle, void** buffer, uint32_t* buffer_num_bytes, MojoWriteDataFlags flags) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_producer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags); } MojoResult Core::EndWriteData(MojoHandle data_pipe_producer_handle, uint32_t num_bytes_written) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_producer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->EndWriteData(num_bytes_written); } MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle, void* elements, uint32_t* num_bytes, MojoReadDataFlags flags) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_consumer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->ReadData(elements, num_bytes, flags); } MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle, const void** buffer, uint32_t* buffer_num_bytes, MojoReadDataFlags flags) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_consumer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->BeginReadData(buffer, buffer_num_bytes, flags); } MojoResult Core::EndReadData(MojoHandle data_pipe_consumer_handle, uint32_t num_bytes_read) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher( GetDispatcher(data_pipe_consumer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; return dispatcher->EndReadData(num_bytes_read); } MojoResult Core::CreateSharedBuffer( const MojoCreateSharedBufferOptions* options, uint64_t num_bytes, MojoHandle* shared_buffer_handle) { RequestContext request_context; MojoCreateSharedBufferOptions validated_options = {}; MojoResult result = SharedBufferDispatcher::ValidateCreateOptions( options, &validated_options); if (result != MOJO_RESULT_OK) return result; scoped_refptr<SharedBufferDispatcher> dispatcher; result = SharedBufferDispatcher::Create( validated_options, GetNodeController(), num_bytes, &dispatcher); if (result != MOJO_RESULT_OK) { DCHECK(!dispatcher); return result; } *shared_buffer_handle = AddDispatcher(dispatcher); if (*shared_buffer_handle == MOJO_HANDLE_INVALID) { LOG(ERROR) << "Handle table full"; dispatcher->Close(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } return MOJO_RESULT_OK; } MojoResult Core::DuplicateBufferHandle( MojoHandle buffer_handle, const MojoDuplicateBufferHandleOptions* options, MojoHandle* new_buffer_handle) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; // Don't verify |options| here; that's the dispatcher's job. scoped_refptr<Dispatcher> new_dispatcher; MojoResult result = dispatcher->DuplicateBufferHandle(options, &new_dispatcher); if (result != MOJO_RESULT_OK) return result; *new_buffer_handle = AddDispatcher(new_dispatcher); if (*new_buffer_handle == MOJO_HANDLE_INVALID) { LOG(ERROR) << "Handle table full"; dispatcher->Close(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } return MOJO_RESULT_OK; } MojoResult Core::MapBuffer(MojoHandle buffer_handle, uint64_t offset, uint64_t num_bytes, void** buffer, MojoMapBufferFlags flags) { RequestContext request_context; scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle)); if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; std::unique_ptr<PlatformSharedBufferMapping> mapping; MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping); if (result != MOJO_RESULT_OK) return result; DCHECK(mapping); void* address = mapping->GetBase(); { base::AutoLock locker(mapping_table_lock_); result = mapping_table_.AddMapping(std::move(mapping)); } if (result != MOJO_RESULT_OK) return result; *buffer = address; return MOJO_RESULT_OK; } MojoResult Core::UnmapBuffer(void* buffer) { RequestContext request_context; base::AutoLock lock(mapping_table_lock_); return mapping_table_.RemoveMapping(buffer); } MojoResult Core::WrapPlatformHandle(const MojoPlatformHandle* platform_handle, MojoHandle* mojo_handle) { ScopedPlatformHandle handle; MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle, &handle); if (result != MOJO_RESULT_OK) return result; return CreatePlatformHandleWrapper(std::move(handle), mojo_handle); } MojoResult Core::UnwrapPlatformHandle(MojoHandle mojo_handle, MojoPlatformHandle* platform_handle) { ScopedPlatformHandle handle; MojoResult result = PassWrappedPlatformHandle(mojo_handle, &handle); if (result != MOJO_RESULT_OK) return result; return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle), platform_handle); } MojoResult Core::WrapPlatformSharedBufferHandle( const MojoPlatformHandle* platform_handle, size_t size, MojoPlatformSharedBufferHandleFlags flags, MojoHandle* mojo_handle) { DCHECK(size); ScopedPlatformHandle handle; MojoResult result = MojoPlatformHandleToScopedPlatformHandle(platform_handle, &handle); if (result != MOJO_RESULT_OK) return result; bool read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY; scoped_refptr<PlatformSharedBuffer> platform_buffer = PlatformSharedBuffer::CreateFromPlatformHandle(size, read_only, std::move(handle)); if (!platform_buffer) return MOJO_RESULT_UNKNOWN; scoped_refptr<SharedBufferDispatcher> dispatcher; result = SharedBufferDispatcher::CreateFromPlatformSharedBuffer( platform_buffer, &dispatcher); if (result != MOJO_RESULT_OK) return result; MojoHandle h = AddDispatcher(dispatcher); if (h == MOJO_HANDLE_INVALID) { dispatcher->Close(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } *mojo_handle = h; return MOJO_RESULT_OK; } MojoResult Core::UnwrapPlatformSharedBufferHandle( MojoHandle mojo_handle, MojoPlatformHandle* platform_handle, size_t* size, MojoPlatformSharedBufferHandleFlags* flags) { scoped_refptr<Dispatcher> dispatcher; MojoResult result = MOJO_RESULT_OK; { base::AutoLock lock(handles_lock_); result = handles_.GetAndRemoveDispatcher(mojo_handle, &dispatcher); if (result != MOJO_RESULT_OK) return result; } if (dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) { dispatcher->Close(); return MOJO_RESULT_INVALID_ARGUMENT; } SharedBufferDispatcher* shm_dispatcher = static_cast<SharedBufferDispatcher*>(dispatcher.get()); scoped_refptr<PlatformSharedBuffer> platform_shared_buffer = shm_dispatcher->PassPlatformSharedBuffer(); CHECK(platform_shared_buffer); CHECK(size); *size = platform_shared_buffer->GetNumBytes(); CHECK(flags); *flags = MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE; if (platform_shared_buffer->IsReadOnly()) *flags |= MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_READ_ONLY; ScopedPlatformHandle handle = platform_shared_buffer->PassPlatformHandle(); return ScopedPlatformHandleToMojoPlatformHandle(std::move(handle), platform_handle); } void Core::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { base::AutoLock lock(handles_lock_); handles_.GetActiveHandlesForTest(handles); } MojoResult Core::WaitManyInternal(const MojoHandle* handles, const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, uint32_t* result_index, HandleSignalsState* signals_states) { CHECK(handles); CHECK(signals); DCHECK_GT(num_handles, 0u); if (result_index) { DCHECK_EQ(*result_index, static_cast<uint32_t>(-1)); } // The primary caller of |WaitManyInternal()| is |Wait()|, which only waits on // a single handle. In the common case of a single handle, this avoid a heap // allocation. base::StackVector<scoped_refptr<Dispatcher>, 1> dispatchers; dispatchers->reserve(num_handles); for (uint32_t i = 0; i < num_handles; i++) { scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]); if (!dispatcher) { if (result_index) *result_index = i; return MOJO_RESULT_INVALID_ARGUMENT; } dispatchers->push_back(dispatcher); } // TODO(vtl): Should make the waiter live (permanently) in TLS. Waiter waiter; waiter.Init(); uint32_t i; MojoResult rv = MOJO_RESULT_OK; for (i = 0; i < num_handles; i++) { rv = dispatchers[i]->AddAwakable( &waiter, signals[i], i, signals_states ? &signals_states[i] : nullptr); if (rv != MOJO_RESULT_OK) { if (result_index) *result_index = i; break; } } uint32_t num_added = i; if (rv == MOJO_RESULT_ALREADY_EXISTS) { rv = MOJO_RESULT_OK; // The i-th one is already "triggered". } else if (rv == MOJO_RESULT_OK) { uintptr_t uintptr_result = *result_index; rv = waiter.Wait(deadline, &uintptr_result); *result_index = static_cast<uint32_t>(uintptr_result); } // Make sure no other dispatchers try to wake |waiter| for the current // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be // destroyed, but this would still be required if the waiter were in TLS.) for (i = 0; i < num_added; i++) { dispatchers[i]->RemoveAwakable( &waiter, signals_states ? &signals_states[i] : nullptr); } if (signals_states) { for (; i < num_handles; i++) signals_states[i] = dispatchers[i]->GetHandleSignalsState(); } return rv; } // static void Core::PassNodeControllerToIOThread( std::unique_ptr<NodeController> node_controller) { // It's OK to leak this reference. At this point we know the IO loop is still // running, and we know the NodeController will observe its eventual // destruction. This tells the NodeController to delete itself when that // happens. node_controller.release()->DestroyOnIOThreadShutdown(); } } // namespace edk } // namespace mojo