summaryrefslogtreecommitdiffstats
path: root/ipc/glue/MiniTransceiver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/MiniTransceiver.cpp')
-rw-r--r--ipc/glue/MiniTransceiver.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/ipc/glue/MiniTransceiver.cpp b/ipc/glue/MiniTransceiver.cpp
new file mode 100644
index 0000000000..933a727f62
--- /dev/null
+++ b/ipc/glue/MiniTransceiver.cpp
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "mozilla/ipc/MiniTransceiver.h"
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "base/eintr_wrapper.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/ScopeExit.h"
+#include "nsDebug.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+
+namespace mozilla::ipc {
+
+static const size_t kMaxIOVecSize = 64;
+static const size_t kMaxDataSize = 8 * 1024;
+static const size_t kMaxNumFds = 16;
+
+MiniTransceiver::MiniTransceiver(int aFd, DataBufferClear aDataBufClear)
+ : mFd(aFd),
+#ifdef DEBUG
+ mState(STATE_NONE),
+#endif
+ mDataBufClear(aDataBufClear) {
+}
+
+namespace {
+
+/**
+ * Initialize the IO vector for sending data and the control buffer for sending
+ * FDs.
+ */
+static void InitMsgHdr(msghdr* aHdr, int aIOVSize, size_t aMaxNumFds) {
+ aHdr->msg_name = nullptr;
+ aHdr->msg_namelen = 0;
+ aHdr->msg_flags = 0;
+
+ // Prepare the IO vector to receive the content of message.
+ auto* iov = new iovec[aIOVSize];
+ aHdr->msg_iov = iov;
+ aHdr->msg_iovlen = aIOVSize;
+
+ // Prepare the control buffer to receive file descriptors.
+ auto* cbuf = new char[CMSG_SPACE(sizeof(int) * aMaxNumFds)];
+ aHdr->msg_control = cbuf;
+ aHdr->msg_controllen = CMSG_SPACE(sizeof(int) * aMaxNumFds);
+}
+
+/**
+ * Delete resources allocated by InitMsgHdr().
+ */
+static void DeinitMsgHdr(msghdr* aHdr) {
+ delete aHdr->msg_iov;
+ delete static_cast<char*>(aHdr->msg_control);
+}
+
+} // namespace
+
+void MiniTransceiver::PrepareFDs(msghdr* aHdr, IPC::Message& aMsg) {
+ // Set control buffer to send file descriptors of the Message.
+ size_t num_fds = aMsg.attached_handles_.Length();
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
+ for (size_t i = 0; i < num_fds; ++i) {
+ reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] =
+ aMsg.attached_handles_[i].get();
+ }
+
+ // This number will be sent in the header of the message. So, we
+ // can check it at the other side.
+ aMsg.header()->num_handles = num_fds;
+}
+
+size_t MiniTransceiver::PrepareBuffers(msghdr* aHdr, IPC::Message& aMsg) {
+ // Set iovec to send for all buffers of the Message.
+ iovec* iov = aHdr->msg_iov;
+ size_t iovlen = 0;
+ size_t bytes_to_send = 0;
+ for (Pickle::BufferList::IterImpl iter(aMsg.Buffers()); !iter.Done();
+ iter.Advance(aMsg.Buffers(), iter.RemainingInSegment())) {
+ char* data = iter.Data();
+ size_t size = iter.RemainingInSegment();
+ iov[iovlen].iov_base = data;
+ iov[iovlen].iov_len = size;
+ iovlen++;
+ MOZ_ASSERT(iovlen <= kMaxIOVecSize);
+ bytes_to_send += size;
+ }
+ MOZ_ASSERT(bytes_to_send <= kMaxDataSize);
+ aHdr->msg_iovlen = iovlen;
+
+ return bytes_to_send;
+}
+
+bool MiniTransceiver::Send(IPC::Message& aMsg) {
+#ifdef DEBUG
+ if (mState == STATE_SENDING) {
+ MOZ_CRASH(
+ "STATE_SENDING: It violates of request-response and no concurrent "
+ "rules");
+ }
+ mState = STATE_SENDING;
+#endif
+
+ auto clean_fdset = MakeScopeExit([&] { aMsg.attached_handles_.Clear(); });
+
+ size_t num_fds = aMsg.attached_handles_.Length();
+ msghdr hdr{};
+ InitMsgHdr(&hdr, kMaxIOVecSize, num_fds);
+
+ UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
+
+ PrepareFDs(&hdr, aMsg);
+ DebugOnly<size_t> bytes_to_send = PrepareBuffers(&hdr, aMsg);
+
+ ssize_t bytes_written = HANDLE_EINTR(sendmsg(mFd, &hdr, 0));
+
+ if (bytes_written < 0) {
+ char error[128];
+ SprintfLiteral(error, "sendmsg: %s", strerror(errno));
+ NS_WARNING(error);
+ return false;
+ }
+ MOZ_ASSERT(bytes_written == (ssize_t)bytes_to_send,
+ "The message is too big?!");
+
+ return true;
+}
+
+unsigned MiniTransceiver::RecvFDs(msghdr* aHdr, int* aAllFds,
+ unsigned aMaxFds) {
+ if (aHdr->msg_controllen == 0) {
+ return 0;
+ }
+
+ unsigned num_all_fds = 0;
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr); cmsg;
+ cmsg = CMSG_NXTHDR(aHdr, cmsg)) {
+ MOZ_ASSERT(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS,
+ "Accept only SCM_RIGHTS to receive file descriptors");
+
+ unsigned payload_sz = cmsg->cmsg_len - CMSG_LEN(0);
+ MOZ_ASSERT(payload_sz % sizeof(int) == 0);
+
+ // Add fds to |aAllFds|
+ unsigned num_part_fds = payload_sz / sizeof(int);
+ int* part_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ MOZ_ASSERT(num_all_fds + num_part_fds <= aMaxFds);
+
+ memcpy(aAllFds + num_all_fds, part_fds, num_part_fds * sizeof(int));
+ num_all_fds += num_part_fds;
+ }
+ return num_all_fds;
+}
+
+bool MiniTransceiver::RecvData(char* aDataBuf, size_t aBufSize,
+ uint32_t* aMsgSize, int* aFdsBuf,
+ unsigned aMaxFds, unsigned* aNumFds) {
+ msghdr hdr;
+ InitMsgHdr(&hdr, 1, aMaxFds);
+
+ UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
+
+ // The buffer to collect all fds received from the socket.
+ int* all_fds = aFdsBuf;
+ unsigned num_all_fds = 0;
+
+ size_t total_readed = 0;
+ uint32_t msgsz = 0;
+ while (msgsz == 0 || total_readed < msgsz) {
+ // Set IO vector with the begin of the unused buffer.
+ hdr.msg_iov->iov_base = aDataBuf + total_readed;
+ hdr.msg_iov->iov_len = (msgsz == 0 ? aBufSize : msgsz) - total_readed;
+
+ // Read the socket
+ ssize_t bytes_readed = HANDLE_EINTR(recvmsg(mFd, &hdr, 0));
+ if (bytes_readed <= 0) {
+ // Closed or error!
+ return false;
+ }
+ total_readed += bytes_readed;
+ MOZ_ASSERT(total_readed <= aBufSize);
+
+ if (msgsz == 0) {
+ // Parse the size of the message.
+ // Get 0 if data in the buffer is no enough to get message size.
+ msgsz = IPC::Message::MessageSize(aDataBuf, aDataBuf + total_readed);
+ }
+
+ num_all_fds += RecvFDs(&hdr, all_fds + num_all_fds, aMaxFds - num_all_fds);
+ }
+
+ *aMsgSize = msgsz;
+ *aNumFds = num_all_fds;
+ return true;
+}
+
+bool MiniTransceiver::Recv(UniquePtr<IPC::Message>& aMsg) {
+#ifdef DEBUG
+ if (mState == STATE_RECEIVING) {
+ MOZ_CRASH(
+ "STATE_RECEIVING: It violates of request-response and no concurrent "
+ "rules");
+ }
+ mState = STATE_RECEIVING;
+#endif
+
+ UniquePtr<char[]> databuf = MakeUnique<char[]>(kMaxDataSize);
+ uint32_t msgsz = 0;
+ int all_fds[kMaxNumFds];
+ unsigned num_all_fds = 0;
+
+ if (!RecvData(databuf.get(), kMaxDataSize, &msgsz, all_fds, kMaxDataSize,
+ &num_all_fds)) {
+ return false;
+ }
+
+ // Create Message from databuf & all_fds.
+ UniquePtr<IPC::Message> msg = MakeUnique<IPC::Message>(databuf.get(), msgsz);
+ nsTArray<UniqueFileHandle> handles(num_all_fds);
+ for (unsigned i = 0; i < num_all_fds; ++i) {
+ handles.AppendElement(UniqueFileHandle(all_fds[i]));
+ }
+ msg->SetAttachedFileHandles(std::move(handles));
+
+ if (mDataBufClear == DataBufferClear::AfterReceiving) {
+ // Avoid content processes from reading the content of
+ // messages.
+ memset(databuf.get(), 0, msgsz);
+ }
+
+ MOZ_ASSERT(msg->header()->num_handles == msg->attached_handles_.Length(),
+ "The number of file descriptors in the header is different from"
+ " the number actually received");
+
+ aMsg = std::move(msg);
+ return true;
+}
+
+} // namespace mozilla::ipc