static already_AddRefed<SharedMemory> ReadSegment(const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes, size_t aExtraSize) { if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) { NS_ERROR("expected 'shmem created' message"); return nullptr; } SharedMemory::SharedMemoryType type; PickleIterator iter(aDescriptor); if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, aNBytes, &type)) { return nullptr; } RefPtr<SharedMemory> segment = NewSegment(type); if (!segment) { return nullptr; } if (!segment->ReadHandle(&aDescriptor, &iter)) { NS_ERROR("trying to open invalid handle"); return nullptr; } aDescriptor.EndRead(iter); size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize); if (!segment->Map(size)) { return nullptr; } // close the handle to the segment after it is mapped segment->CloseHandle(); return segment.forget(); }
/* static */ already_AddRefed<nsIEventTarget> nsIContentChild::GetConstructedEventTarget(const IPC::Message& aMsg) { ActorHandle handle; TabId tabId, sameTabGroupAs; PickleIterator iter(aMsg); if (!IPC::ReadParam(&aMsg, &iter, &handle)) { return nullptr; } aMsg.IgnoreSentinel(&iter); if (!IPC::ReadParam(&aMsg, &iter, &tabId)) { return nullptr; } aMsg.IgnoreSentinel(&iter); if (!IPC::ReadParam(&aMsg, &iter, &sameTabGroupAs)) { return nullptr; } // If sameTabGroupAs is non-zero, then the new tab will be in the same // TabGroup as a previously created tab. Rather than try to find the // previously created tab (whose constructor message may not even have been // processed yet, in theory) and look up its event target, we just use the // default event target. This means that runnables for this tab will not be // labeled. However, this path is only taken for print preview and view // source, which are not performance-sensitive. if (sameTabGroupAs) { return nullptr; } // If the request for a new TabChild is coming from the parent process, then // there is no opener. Therefore, we create a fresh TabGroup. RefPtr<TabGroup> tabGroup = new TabGroup(); nsCOMPtr<nsIEventTarget> target = tabGroup->EventTargetFor(TaskCategory::Other); return target.forget(); }
bool Process::Send(uint32_t id, uint32_t size, const uint8_t* data) { // mutex from kill Helium::MutexScopeLock mutex ( m_KillMutex ); if ( m_Connection && m_Connection->GetState() == IPC::ConnectionStates::Active ) { IPC::Message* msg = m_Connection->CreateMessage(id, data ? size : 0); if (data && size) { memcpy(msg->GetData(), data, size); } if (m_Connection->Send(msg) == IPC::ConnectionStates::Active) { return true; } else { delete msg; } } return false; }
// static already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, const IPC::Message& aDescriptor, id_t* aId, bool /*unused*/) { if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) { return nullptr; } SharedMemory::SharedMemoryType type; void* iter = nullptr; size_t size; if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type)) return nullptr; nsRefPtr<SharedMemory> segment; size_t segmentSize = SharedMemory::PageAlignedSize(size + sizeof(uint32_t)); if (SharedMemory::TYPE_BASIC == type) { SharedMemoryBasic::Handle handle; if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle)) return nullptr; if (!SharedMemoryBasic::IsHandleValid(handle)) { return nullptr; } segment = CreateSegment(segmentSize, handle); } #ifdef MOZ_HAVE_SHAREDMEMORYSYSV else if (SharedMemory::TYPE_SYSV == type) { SharedMemorySysV::Handle handle; if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle)) return nullptr; if (!SharedMemorySysV::IsHandleValid(handle)) { return nullptr; } segment = CreateSegment(segmentSize, handle); } #endif else { return nullptr; } if (!segment) return nullptr; // this is the only validity check done in non-DEBUG builds if (size != static_cast<size_t>(*PtrToSize(segment))) { return nullptr; } return segment.forget(); }
static bool Read(const IPC::Message& aMsg, TransportDescriptor* aDescriptor, ProcessId* aOtherProcess, ProtocolId* aProtocol) { void* iter = nullptr; if (!IPC::ReadParam(&aMsg, &iter, aDescriptor) || !IPC::ReadParam(&aMsg, &iter, aOtherProcess) || !IPC::ReadParam(&aMsg, &iter, reinterpret_cast<uint32_t*>(aProtocol))) { return false; } aMsg.EndRead(iter); return true; }
// static already_AddRefed<Shmem::SharedMemory> Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead, const IPC::Message& aDescriptor, id_t* aId, bool aProtect) { if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) { NS_ERROR("expected 'shmem created' message"); return nullptr; } void* iter = nullptr; SharedMemory::SharedMemoryType type; size_t size; if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, &size, &type)) return nullptr; nsRefPtr<SharedMemory> segment; size_t pageSize = SharedMemory::SystemPageSize(); // |2*pageSize| is for the front and back sentinels size_t segmentSize = SharedMemory::PageAlignedSize(size + 2*pageSize); if (SharedMemory::TYPE_BASIC == type) { SharedMemoryBasic::Handle handle; if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle)) return nullptr; if (!SharedMemoryBasic::IsHandleValid(handle)) { NS_ERROR("trying to open invalid handle"); return nullptr; } segment = CreateSegment(segmentSize, handle); } #ifdef MOZ_HAVE_SHAREDMEMORYSYSV else if (SharedMemory::TYPE_SYSV == type) { SharedMemorySysV::Handle handle; if (!ShmemCreated::ReadHandle(&aDescriptor, &iter, &handle)) return nullptr; if (!SharedMemorySysV::IsHandleValid(handle)) { NS_ERROR("trying to open invalid handle"); return nullptr; } segment = CreateSegment(segmentSize, handle); } #endif else { NS_ERROR("unknown shmem type"); return nullptr; } if (!segment) return nullptr; Header* header = GetHeader(segment); if (size != header->mSize) { NS_ERROR("Wrong size for this Shmem!"); return nullptr; } // The caller of this function may not know whether the segment is // unsafe or not if (!header->mUnsafe && aProtect) Protect(segment); return segment.forget(); }
bool Host::Process(bool wait) { bool result = true; if (Connected()) { if (m_ConnectionCount != m_Connection->GetConnectCount()) { #ifdef RPC_DEBUG printf("RPC::Connection cycled, resetting stack\n"); #endif m_ConnectionCount = m_Connection->GetConnectCount(); m_Stack.Reset(); } if (m_Connection->GetState() != IPC::ConnectionStates::Active) { result = false; } while (result) { IPC::Message* msg = NULL; IPC::ConnectionState state = m_Connection->Receive(&msg, wait); if (state != IPC::ConnectionStates::Active || msg == NULL) { result = false; break; } #ifdef RPC_DEBUG_MSG printf("RPC::Got message id 0x%08x, size %d, transaction %d\n", msg->GetID(), msg->GetSize(), msg->GetTransaction()); #endif bool is_reply = m_Connection->CreatedMessage(msg->GetTransaction()); if (is_reply && m_Stack.Size() > 0) { Frame* top = m_Stack.Top(); bool is_current = msg->GetTransaction() == top->m_ReplyTransaction; if (is_current) { #ifdef RPC_DEBUG printf("RPC::Got reply to transaction %d\n", msg->GetTransaction()); #endif // subsume the message into the frame top->m_Replied = true; top->m_ReplyID = msg->GetID(); top->m_ReplyData = msg->TakeData(); // taking this will disconnect it from the message, making delete below *safe* top->m_ReplySize = msg->GetSize(); // free msg delete msg; // we have our reply, break out of processing messages break; } else { printf("RPC::Got reply to transaction %d, however its not a reply for the top of the stack (stack size: %d)\n", msg->GetTransaction(), m_Stack.Size()); } } else // else this is not a reply, meaning this is a new invocation { int32_t size HELIUM_ASSERT_ONLY = m_Stack.Size(); // allocate a frame for this local call Frame* frame = m_Stack.Push(); frame->m_ReplyTransaction = msg->GetTransaction(); #ifdef RPC_DEBUG printf("RPC::Pushing invocation transaction %d, stack size %d\n", frame->m_ReplyTransaction, m_Stack.Size()); #endif // the one and only call to invoke, this expects our frame to be allocated if (Invoke(msg)) { #ifdef RPC_DEBUG printf("RPC::Popping invocation transaction %d, stack size %d\n", frame->m_ReplyTransaction, m_Stack.Size()); #endif // success, pop the call m_Stack.Pop(); HELIUM_ASSERT(size == m_Stack.Size()); } else { printf("RPC::Invocation failed, resetting stack\n"); m_Stack.Reset(); } } } } else { result = false; } return result; }
bool Host::Invoke(IPC::Message* msg) { #pragma TODO("Unpack the invoker and interface name from the message data") const char* invokerName = NULL; const char* interfaceName = NULL; // find the interface Interface* interface = GetInterface(interfaceName); if (interface == NULL) { printf("RPC::Unable to find interface '%s'\n", interfaceName); delete msg; return true; } // find the invoker Invoker* invoker = interface->GetInvoker(invokerName); if (invoker == NULL) { printf("RPC::Unable to find invoker '%s' in interface '%s'\n", invokerName, interfaceName); delete msg; return true; } // get our frame from the top of the stack Frame* frame = m_Stack.Top(); HELIUM_ASSERT(frame->m_Message == NULL); frame->m_Message = msg; // call the function frame->m_MessageTaken = false; invoker->Invoke(msg->GetData(), msg->GetSize()); HELIUM_ASSERT(frame->m_Message != NULL); frame->m_Message = NULL; Args* args = (Args*)msg->GetData(); if (args->m_Flags & RPC::Flags::NonBlocking) { if (!frame->m_MessageTaken) { delete msg; } return true; // async call, we are done } // our reply IPC::Message* reply = NULL; // if we have data, and a reference args or payload if (msg->GetSize() > 0 && args->m_Flags & (RPC::Flags::ReplyWithArgs | RPC::Flags::ReplyWithPayload)) { // total size of reply uint32_t size = 0; // size of args section uint32_t argSize = invoker->GetArgsSize(); // size of payload section uint32_t payload_size = msg->GetSize() - argSize; // if we have a ref args if (args->m_Flags & RPC::Flags::ReplyWithArgs) { // alloc for args block size += argSize; } // if we have a ref payload if (args->m_Flags & RPC::Flags::ReplyWithPayload) { // alloc for payload block size += payload_size; } // create message reply = Create(invoker, size, msg->GetTransaction()); // where to write uint8_t* ptr = reply->GetData(); // if we have a ref args if (args->m_Flags & RPC::Flags::ReplyWithArgs) { if (Swizzle()) { invoker->Swizzle( msg->GetData() ); } // write to ptr memcpy(ptr, msg->GetData(), argSize); // incr ptr by amount written ptr += argSize; } // if we have a ref payload if (args->m_Flags & RPC::Flags::ReplyWithPayload) { // write to ptr memcpy(ptr, msg->GetData() + argSize, payload_size); // incr ptr by amount written ptr += payload_size; } // assert we did not overrun message size HELIUM_ASSERT((uint32_t)(ptr - reply->GetData()) == size); } else // no data, or no ref args or payload { // just create an empty reply, the other side is blocking reply = Create(invoker, 0, msg->GetTransaction()); } if (m_Connection->Send(reply)!= IPC::ConnectionStates::Active) { delete reply; } #ifdef RPC_DEBUG_MSG printf("RPC::Put message id 0x%08x, size %d, transaction %d\n", reply->GetID(), reply->GetSize(), reply->GetTransaction()); #endif if (!frame->m_MessageTaken) { delete msg; } return true; }
void Host::Emit(Invoker* invoker, Args* args, uint32_t size, SwizzleFunc swizzler) { if (Connected()) { if (m_ConnectionCount != m_Connection->GetConnectCount()) { #ifdef RPC_DEBUG printf("RPC::Connection cycled, resetting stack\n"); #endif m_ConnectionCount = m_Connection->GetConnectCount(); m_Stack.Reset(); } uint32_t size = 0; if (args != NULL) { HELIUM_ASSERT(size > 0); size += size; } if (args->m_Payload != NULL) { HELIUM_ASSERT(args->m_PayloadSize > 0); size += args->m_PayloadSize; } IPC::Message* message = Create(invoker, size); uint8_t* ptr = message->GetData(); if (args != NULL) { if (Swizzle()) { swizzler(args); } memcpy(ptr, args, size); ptr += size; if (Swizzle()) { swizzler(args); } } if (args->m_Payload != NULL) { memcpy(ptr, args->m_Payload, args->m_PayloadSize); ptr += args->m_PayloadSize; } HELIUM_ASSERT((uint32_t)(ptr - message->GetData()) == size); #ifdef RPC_DEBUG_MSG uint32_t msg_id = message->GetID(); uint32_t msg_size = message->GetSize(); #endif int32_t msg_transaction = message->GetTransaction(); if (m_Connection->Send(message)!=IPC::ConnectionStates::Active) { delete message; return; } #ifdef RPC_DEBUG_MSG printf("RPC::Put message id 0x%08x, size %d, transaction %d\n", msg_id, msg_size, msg_transaction); #endif message = NULL; // assume its GONE if (args->m_Flags & RPC::Flags::NonBlocking) { #ifdef RPC_DEBUG printf("RPC::Emitting async transaction %d\n", msg_transaction); #endif } else { // create frame for call Frame* frame = m_Stack.Push(); // set the transaction we are blocking on frame->m_ReplyTransaction = msg_transaction; #ifdef RPC_DEBUG printf("RPC::Emitting transaction %d, stack size %d\n", msg_transaction, m_Stack.Size()); #endif // process until we retrieve it frame->m_Replied = false; // process messages until we receive our reply while (Process(true) && !frame->m_Replied); // if we did not get our reply if (!frame->m_Replied) { #ifdef RPC_DEBUG printf("RPC::Emit failed for transaction %d, stack size %d\n", msg_transaction, m_Stack.Size()); #endif // if we didn't reset and the call timed out, pop if (m_Stack.Size()) { m_Stack.Pop(); } } else { #ifdef RPC_DEBUG printf("RPC::Emit success for transaction %d, stack size %d\n", msg_transaction, m_Stack.Size()); #endif size = 0; ptr = frame->m_ReplyData; if (args != NULL) { HELIUM_ASSERT(size > 0); size += size; } if (args->m_Payload != NULL) { HELIUM_ASSERT(args->m_PayloadSize > 0); size += args->m_PayloadSize; } // do ref args processing here if (args->m_Flags & RPC::Flags::ReplyWithArgs && args != NULL) { if (Swizzle()) { swizzler(ptr); } // copy our data BACK memcpy(args, ptr, size); ptr += size; } // do ref payload processing here if (args->m_Flags & RPC::Flags::ReplyWithPayload && args->m_Payload != NULL) { // copy our data BACK memcpy(args->m_Payload, ptr, args->m_PayloadSize); ptr += args->m_PayloadSize; } // clean up our reply message's memory delete[] frame->m_ReplyData; // call complete, pop m_Stack.Pop(); } } } }