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"
16 #include <sys/types.h>
17 #include <sys/socket.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
)
32 mDataBufClear(aDataBufClear
) {
38 * Initialize the IO vector for sending data and the control buffer for sending
41 static void InitMsgHdr(msghdr
* aHdr
, int aIOVSize
, size_t aMaxNumFds
) {
42 aHdr
->msg_name
= nullptr;
43 aHdr
->msg_namelen
= 0;
46 // Prepare the IO vector to receive the content of message.
47 auto* iov
= new iovec
[aIOVSize
];
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
;
62 * Delete resources allocated by InitMsgHdr().
64 static void DeinitMsgHdr(msghdr
* aHdr
) {
66 delete static_cast<char*>(aHdr
->msg_control
);
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
;
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
;
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
) {
112 if (mState
== STATE_SENDING
) {
114 "STATE_SENDING: It violates of request-response and no concurrent "
117 mState
= STATE_SENDING
;
120 auto clean_fdset
= MakeScopeExit([&] { aMsg
.attached_handles_
.Clear(); });
122 size_t num_fds
= aMsg
.attached_handles_
.Length();
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) {
135 SprintfLiteral(error
, "sendmsg: %s", strerror(errno
));
139 MOZ_ASSERT(bytes_written
== (ssize_t
)bytes_to_send
,
140 "The message is too big?!");
145 unsigned MiniTransceiver::RecvFDs(msghdr
* aHdr
, int* aAllFds
,
147 if (aHdr
->msg_controllen
== 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
;
171 bool MiniTransceiver::RecvData(char* aDataBuf
, size_t aBufSize
,
172 uint32_t* aMsgSize
, int* aFdsBuf
,
173 unsigned aMaxFds
, unsigned* aNumFds
) {
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;
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
;
191 ssize_t bytes_readed
= HANDLE_EINTR(recvmsg(mFd
, &hdr
, 0));
192 if (bytes_readed
<= 0) {
196 total_readed
+= bytes_readed
;
197 MOZ_ASSERT(total_readed
<= aBufSize
);
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
);
209 *aNumFds
= num_all_fds
;
213 bool MiniTransceiver::Recv(UniquePtr
<IPC::Message
>& aMsg
) {
215 if (mState
== STATE_RECEIVING
) {
217 "STATE_RECEIVING: It violates of request-response and no concurrent "
220 mState
= STATE_RECEIVING
;
223 UniquePtr
<char[]> databuf
= MakeUnique
<char[]>(kMaxDataSize
);
225 int all_fds
[kMaxNumFds
];
226 unsigned num_all_fds
= 0;
228 if (!RecvData(databuf
.get(), kMaxDataSize
, &msgsz
, all_fds
, kMaxDataSize
,
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
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
);
255 } // namespace mozilla::ipc