Backed out changeset 76f7599ecbfa (bug 1914545) as requested by developer for causing...
[gecko.git] / ipc / glue / MiniTransceiver.cpp
blobefb12d2fe2c4c80c10f6898e446597fff978074a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ipc/MiniTransceiver.h"
7 #include "chrome/common/ipc_message.h"
8 #include "chrome/common/ipc_message_utils.h"
9 #include "base/eintr_wrapper.h"
10 #include "mozilla/UniquePtr.h"
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/Sprintf.h"
13 #include "mozilla/ScopeExit.h"
14 #include "nsDebug.h"
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <string.h>
19 #include <errno.h>
21 namespace mozilla::ipc {
23 static const size_t kMaxIOVecSize = 64;
24 static const size_t kMaxDataSize = 8 * 1024;
25 static const size_t kMaxNumFds = 16;
27 MiniTransceiver::MiniTransceiver(int aFd, DataBufferClear aDataBufClear)
28 : mFd(aFd),
29 #ifdef DEBUG
30 mState(STATE_NONE),
31 #endif
32 mDataBufClear(aDataBufClear) {
35 namespace {
37 /**
38 * Initialize the IO vector for sending data and the control buffer for sending
39 * FDs.
41 static void InitMsgHdr(msghdr* aHdr, int aIOVSize, size_t aMaxNumFds) {
42 aHdr->msg_name = nullptr;
43 aHdr->msg_namelen = 0;
44 aHdr->msg_flags = 0;
46 // Prepare the IO vector to receive the content of message.
47 auto* iov = new iovec[aIOVSize];
48 aHdr->msg_iov = iov;
49 aHdr->msg_iovlen = aIOVSize;
51 // Prepare the control buffer to receive file descriptors.
52 const size_t cbufSize = CMSG_SPACE(sizeof(int) * aMaxNumFds);
53 auto* cbuf = new char[cbufSize];
54 // Avoid valgrind complaints about uninitialized padding (but also,
55 // fill with a value that isn't a valid fd, just in case).
56 memset(cbuf, 255, cbufSize);
57 aHdr->msg_control = cbuf;
58 aHdr->msg_controllen = cbufSize;
61 /**
62 * Delete resources allocated by InitMsgHdr().
64 static void DeinitMsgHdr(msghdr* aHdr) {
65 delete aHdr->msg_iov;
66 delete static_cast<char*>(aHdr->msg_control);
69 } // namespace
71 void MiniTransceiver::PrepareFDs(msghdr* aHdr, IPC::Message& aMsg) {
72 // Set control buffer to send file descriptors of the Message.
73 size_t num_fds = aMsg.attached_handles_.Length();
75 cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr);
76 cmsg->cmsg_level = SOL_SOCKET;
77 cmsg->cmsg_type = SCM_RIGHTS;
78 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
79 for (size_t i = 0; i < num_fds; ++i) {
80 reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] =
81 aMsg.attached_handles_[i].get();
84 // This number will be sent in the header of the message. So, we
85 // can check it at the other side.
86 aMsg.header()->num_handles = num_fds;
89 size_t MiniTransceiver::PrepareBuffers(msghdr* aHdr, IPC::Message& aMsg) {
90 // Set iovec to send for all buffers of the Message.
91 iovec* iov = aHdr->msg_iov;
92 size_t iovlen = 0;
93 size_t bytes_to_send = 0;
94 for (Pickle::BufferList::IterImpl iter(aMsg.Buffers()); !iter.Done();
95 iter.Advance(aMsg.Buffers(), iter.RemainingInSegment())) {
96 char* data = iter.Data();
97 size_t size = iter.RemainingInSegment();
98 iov[iovlen].iov_base = data;
99 iov[iovlen].iov_len = size;
100 iovlen++;
101 MOZ_ASSERT(iovlen <= kMaxIOVecSize);
102 bytes_to_send += size;
104 MOZ_ASSERT(bytes_to_send <= kMaxDataSize);
105 aHdr->msg_iovlen = iovlen;
107 return bytes_to_send;
110 bool MiniTransceiver::Send(IPC::Message& aMsg) {
111 #ifdef DEBUG
112 if (mState == STATE_SENDING) {
113 MOZ_CRASH(
114 "STATE_SENDING: It violates of request-response and no concurrent "
115 "rules");
117 mState = STATE_SENDING;
118 #endif
120 auto clean_fdset = MakeScopeExit([&] { aMsg.attached_handles_.Clear(); });
122 size_t num_fds = aMsg.attached_handles_.Length();
123 msghdr hdr{};
124 InitMsgHdr(&hdr, kMaxIOVecSize, num_fds);
126 UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
128 PrepareFDs(&hdr, aMsg);
129 DebugOnly<size_t> bytes_to_send = PrepareBuffers(&hdr, aMsg);
131 ssize_t bytes_written = HANDLE_EINTR(sendmsg(mFd, &hdr, 0));
133 if (bytes_written < 0) {
134 char error[128];
135 SprintfLiteral(error, "sendmsg: %s", strerror(errno));
136 NS_WARNING(error);
137 return false;
139 MOZ_ASSERT(bytes_written == (ssize_t)bytes_to_send,
140 "The message is too big?!");
142 return true;
145 unsigned MiniTransceiver::RecvFDs(msghdr* aHdr, int* aAllFds,
146 unsigned aMaxFds) {
147 if (aHdr->msg_controllen == 0) {
148 return 0;
151 unsigned num_all_fds = 0;
152 for (cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr); cmsg;
153 cmsg = CMSG_NXTHDR(aHdr, cmsg)) {
154 MOZ_ASSERT(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS,
155 "Accept only SCM_RIGHTS to receive file descriptors");
157 unsigned payload_sz = cmsg->cmsg_len - CMSG_LEN(0);
158 MOZ_ASSERT(payload_sz % sizeof(int) == 0);
160 // Add fds to |aAllFds|
161 unsigned num_part_fds = payload_sz / sizeof(int);
162 int* part_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
163 MOZ_ASSERT(num_all_fds + num_part_fds <= aMaxFds);
165 memcpy(aAllFds + num_all_fds, part_fds, num_part_fds * sizeof(int));
166 num_all_fds += num_part_fds;
168 return num_all_fds;
171 bool MiniTransceiver::RecvData(char* aDataBuf, size_t aBufSize,
172 uint32_t* aMsgSize, int* aFdsBuf,
173 unsigned aMaxFds, unsigned* aNumFds) {
174 msghdr hdr;
175 InitMsgHdr(&hdr, 1, aMaxFds);
177 UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
179 // The buffer to collect all fds received from the socket.
180 int* all_fds = aFdsBuf;
181 unsigned num_all_fds = 0;
183 size_t total_readed = 0;
184 uint32_t msgsz = 0;
185 while (msgsz == 0 || total_readed < msgsz) {
186 // Set IO vector with the begin of the unused buffer.
187 hdr.msg_iov->iov_base = aDataBuf + total_readed;
188 hdr.msg_iov->iov_len = (msgsz == 0 ? aBufSize : msgsz) - total_readed;
190 // Read the socket
191 ssize_t bytes_readed = HANDLE_EINTR(recvmsg(mFd, &hdr, 0));
192 if (bytes_readed <= 0) {
193 // Closed or error!
194 return false;
196 total_readed += bytes_readed;
197 MOZ_ASSERT(total_readed <= aBufSize);
199 if (msgsz == 0) {
200 // Parse the size of the message.
201 // Get 0 if data in the buffer is no enough to get message size.
202 msgsz = IPC::Message::MessageSize(aDataBuf, aDataBuf + total_readed);
205 num_all_fds += RecvFDs(&hdr, all_fds + num_all_fds, aMaxFds - num_all_fds);
208 *aMsgSize = msgsz;
209 *aNumFds = num_all_fds;
210 return true;
213 bool MiniTransceiver::Recv(UniquePtr<IPC::Message>& aMsg) {
214 #ifdef DEBUG
215 if (mState == STATE_RECEIVING) {
216 MOZ_CRASH(
217 "STATE_RECEIVING: It violates of request-response and no concurrent "
218 "rules");
220 mState = STATE_RECEIVING;
221 #endif
223 UniquePtr<char[]> databuf = MakeUnique<char[]>(kMaxDataSize);
224 uint32_t msgsz = 0;
225 int all_fds[kMaxNumFds];
226 unsigned num_all_fds = 0;
228 if (!RecvData(databuf.get(), kMaxDataSize, &msgsz, all_fds, kMaxDataSize,
229 &num_all_fds)) {
230 return false;
233 // Create Message from databuf & all_fds.
234 UniquePtr<IPC::Message> msg = MakeUnique<IPC::Message>(databuf.get(), msgsz);
235 nsTArray<UniqueFileHandle> handles(num_all_fds);
236 for (unsigned i = 0; i < num_all_fds; ++i) {
237 handles.AppendElement(UniqueFileHandle(all_fds[i]));
239 msg->SetAttachedFileHandles(std::move(handles));
241 if (mDataBufClear == DataBufferClear::AfterReceiving) {
242 // Avoid content processes from reading the content of
243 // messages.
244 memset(databuf.get(), 0, msgsz);
247 MOZ_ASSERT(msg->header()->num_handles == msg->attached_handles_.Length(),
248 "The number of file descriptors in the header is different from"
249 " the number actually received");
251 aMsg = std::move(msg);
252 return true;
255 } // namespace mozilla::ipc