1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2011 Igalia S.L.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "Connection.h"
30
31#include "DataReference.h"
32#include "SharedMemory.h"
33#include "UnixMessage.h"
34#include <sys/socket.h>
35#include <unistd.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <poll.h>
39#include <wtf/Assertions.h>
40#include <wtf/StdLibExtras.h>
41#include <wtf/UniStdExtras.h>
42
43#if USE(GLIB)
44#include <gio/gio.h>
45#endif
46
47// Although it's available on Darwin, SOCK_SEQPACKET seems to work differently
48// than in traditional Unix so fallback to STREAM on that platform.
49#if defined(SOCK_SEQPACKET) && !OS(DARWIN)
50#define SOCKET_TYPE SOCK_SEQPACKET
51#else
52#if USE(GLIB)
53#define SOCKET_TYPE SOCK_STREAM
54#else
55#define SOCKET_TYPE SOCK_DGRAM
56#endif
57#endif // SOCK_SEQPACKET
58
59namespace IPC {
60
61static const size_t messageMaxSize = 4096;
62static const size_t attachmentMaxAmount = 254;
63
64class AttachmentInfo {
65 WTF_MAKE_FAST_ALLOCATED;
66public:
67 AttachmentInfo() = default;
68
69 void setType(Attachment::Type type) { m_type = type; }
70 Attachment::Type type() const { return m_type; }
71 void setSize(size_t size)
72 {
73 ASSERT(m_type == Attachment::MappedMemoryType);
74 m_size = size;
75 }
76
77 size_t size() const
78 {
79 ASSERT(m_type == Attachment::MappedMemoryType);
80 return m_size;
81 }
82
83 // The attachment is not null unless explicitly set.
84 void setNull() { m_isNull = true; }
85 bool isNull() const { return m_isNull; }
86
87private:
88 Attachment::Type m_type { Attachment::Uninitialized };
89 bool m_isNull { false };
90 size_t m_size { 0 };
91};
92
93static_assert(sizeof(MessageInfo) + sizeof(AttachmentInfo) * attachmentMaxAmount <= messageMaxSize, "messageMaxSize is too small.");
94
95void Connection::platformInitialize(Identifier identifier)
96{
97 m_socketDescriptor = identifier;
98#if USE(GLIB)
99 m_socket = adoptGRef(g_socket_new_from_fd(m_socketDescriptor, nullptr));
100#endif
101 m_readBuffer.reserveInitialCapacity(messageMaxSize);
102 m_fileDescriptors.reserveInitialCapacity(attachmentMaxAmount);
103}
104
105void Connection::platformInvalidate()
106{
107#if USE(GLIB)
108 // In the GLib platform the socket descriptor is owned by GSocket.
109 m_socket = nullptr;
110#else
111 if (m_socketDescriptor != -1)
112 closeWithRetry(m_socketDescriptor);
113#endif
114
115 if (!m_isConnected)
116 return;
117
118#if USE(GLIB)
119 m_readSocketMonitor.stop();
120 m_writeSocketMonitor.stop();
121#endif
122
123 m_socketDescriptor = -1;
124 m_isConnected = false;
125}
126
127bool Connection::processMessage()
128{
129 if (m_readBuffer.size() < sizeof(MessageInfo))
130 return false;
131
132 uint8_t* messageData = m_readBuffer.data();
133 MessageInfo messageInfo;
134 memcpy(&messageInfo, messageData, sizeof(messageInfo));
135 messageData += sizeof(messageInfo);
136
137 if (messageInfo.attachmentCount() > attachmentMaxAmount || (!messageInfo.isBodyOutOfLine() && messageInfo.bodySize() > messageMaxSize)) {
138 ASSERT_NOT_REACHED();
139 return false;
140 }
141
142 size_t messageLength = sizeof(MessageInfo) + messageInfo.attachmentCount() * sizeof(AttachmentInfo) + (messageInfo.isBodyOutOfLine() ? 0 : messageInfo.bodySize());
143 if (m_readBuffer.size() < messageLength)
144 return false;
145
146 size_t attachmentFileDescriptorCount = 0;
147 size_t attachmentCount = messageInfo.attachmentCount();
148 Vector<AttachmentInfo> attachmentInfo(attachmentCount);
149
150 if (attachmentCount) {
151 memcpy(attachmentInfo.data(), messageData, sizeof(AttachmentInfo) * attachmentCount);
152 messageData += sizeof(AttachmentInfo) * attachmentCount;
153
154 for (size_t i = 0; i < attachmentCount; ++i) {
155 switch (attachmentInfo[i].type()) {
156 case Attachment::MappedMemoryType:
157 case Attachment::SocketType:
158 if (!attachmentInfo[i].isNull())
159 attachmentFileDescriptorCount++;
160 break;
161 case Attachment::Uninitialized:
162 default:
163 break;
164 }
165 }
166
167 if (messageInfo.isBodyOutOfLine())
168 attachmentCount--;
169 }
170
171 Vector<Attachment> attachments(attachmentCount);
172 RefPtr<WebKit::SharedMemory> oolMessageBody;
173
174 size_t fdIndex = 0;
175 for (size_t i = 0; i < attachmentCount; ++i) {
176 int fd = -1;
177 switch (attachmentInfo[i].type()) {
178 case Attachment::MappedMemoryType:
179 if (!attachmentInfo[i].isNull())
180 fd = m_fileDescriptors[fdIndex++];
181 attachments[attachmentCount - i - 1] = Attachment(fd, attachmentInfo[i].size());
182 break;
183 case Attachment::SocketType:
184 if (!attachmentInfo[i].isNull())
185 fd = m_fileDescriptors[fdIndex++];
186 attachments[attachmentCount - i - 1] = Attachment(fd);
187 break;
188 case Attachment::Uninitialized:
189 attachments[attachmentCount - i - 1] = Attachment();
190 default:
191 break;
192 }
193 }
194
195 if (messageInfo.isBodyOutOfLine()) {
196 ASSERT(messageInfo.bodySize());
197
198 if (attachmentInfo[attachmentCount].isNull() || attachmentInfo[attachmentCount].size() != messageInfo.bodySize()) {
199 ASSERT_NOT_REACHED();
200 return false;
201 }
202
203 WebKit::SharedMemory::Handle handle;
204 handle.adoptAttachment(IPC::Attachment(m_fileDescriptors[attachmentFileDescriptorCount - 1], attachmentInfo[attachmentCount].size()));
205
206 oolMessageBody = WebKit::SharedMemory::map(handle, WebKit::SharedMemory::Protection::ReadOnly);
207 if (!oolMessageBody) {
208 ASSERT_NOT_REACHED();
209 return false;
210 }
211 }
212
213 ASSERT(attachments.size() == (messageInfo.isBodyOutOfLine() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()));
214
215 uint8_t* messageBody = messageData;
216 if (messageInfo.isBodyOutOfLine())
217 messageBody = reinterpret_cast<uint8_t*>(oolMessageBody->data());
218
219 auto decoder = std::make_unique<Decoder>(messageBody, messageInfo.bodySize(), nullptr, WTFMove(attachments));
220
221 processIncomingMessage(WTFMove(decoder));
222
223 if (m_readBuffer.size() > messageLength) {
224 memmove(m_readBuffer.data(), m_readBuffer.data() + messageLength, m_readBuffer.size() - messageLength);
225 m_readBuffer.shrink(m_readBuffer.size() - messageLength);
226 } else
227 m_readBuffer.shrink(0);
228
229 if (attachmentFileDescriptorCount) {
230 if (m_fileDescriptors.size() > attachmentFileDescriptorCount) {
231 memmove(m_fileDescriptors.data(), m_fileDescriptors.data() + attachmentFileDescriptorCount, (m_fileDescriptors.size() - attachmentFileDescriptorCount) * sizeof(int));
232 m_fileDescriptors.shrink(m_fileDescriptors.size() - attachmentFileDescriptorCount);
233 } else
234 m_fileDescriptors.shrink(0);
235 }
236
237
238 return true;
239}
240
241static ssize_t readBytesFromSocket(int socketDescriptor, Vector<uint8_t>& buffer, Vector<int>& fileDescriptors)
242{
243 struct msghdr message;
244 memset(&message, 0, sizeof(message));
245
246 struct iovec iov[1];
247 memset(&iov, 0, sizeof(iov));
248
249 message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentMaxAmount);
250 MallocPtr<char> attachmentDescriptorBuffer = MallocPtr<char>::malloc(sizeof(char) * message.msg_controllen);
251 memset(attachmentDescriptorBuffer.get(), 0, sizeof(char) * message.msg_controllen);
252 message.msg_control = attachmentDescriptorBuffer.get();
253
254 size_t previousBufferSize = buffer.size();
255 buffer.grow(buffer.capacity());
256 iov[0].iov_base = buffer.data() + previousBufferSize;
257 iov[0].iov_len = buffer.size() - previousBufferSize;
258
259 message.msg_iov = iov;
260 message.msg_iovlen = 1;
261
262 while (true) {
263 ssize_t bytesRead = recvmsg(socketDescriptor, &message, 0);
264
265 if (bytesRead < 0) {
266 if (errno == EINTR)
267 continue;
268
269 buffer.shrink(previousBufferSize);
270 return -1;
271 }
272
273 struct cmsghdr* controlMessage;
274 for (controlMessage = CMSG_FIRSTHDR(&message); controlMessage; controlMessage = CMSG_NXTHDR(&message, controlMessage)) {
275 if (controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) {
276 if (controlMessage->cmsg_len < CMSG_LEN(0) || controlMessage->cmsg_len > CMSG_LEN(sizeof(int) * attachmentMaxAmount)) {
277 ASSERT_NOT_REACHED();
278 break;
279 }
280 size_t previousFileDescriptorsSize = fileDescriptors.size();
281 size_t fileDescriptorsCount = (controlMessage->cmsg_len - CMSG_LEN(0)) / sizeof(int);
282 fileDescriptors.grow(fileDescriptors.size() + fileDescriptorsCount);
283 memcpy(fileDescriptors.data() + previousFileDescriptorsSize, CMSG_DATA(controlMessage), sizeof(int) * fileDescriptorsCount);
284
285 for (size_t i = 0; i < fileDescriptorsCount; ++i) {
286 if (!setCloseOnExec(fileDescriptors[previousFileDescriptorsSize + i])) {
287 ASSERT_NOT_REACHED();
288 break;
289 }
290 }
291 break;
292 }
293 }
294
295 buffer.shrink(previousBufferSize + bytesRead);
296 return bytesRead;
297 }
298
299 return -1;
300}
301
302void Connection::readyReadHandler()
303{
304 while (true) {
305 ssize_t bytesRead = readBytesFromSocket(m_socketDescriptor, m_readBuffer, m_fileDescriptors);
306
307 if (bytesRead < 0) {
308 // EINTR was already handled by readBytesFromSocket.
309 if (errno == EAGAIN || errno == EWOULDBLOCK)
310 return;
311
312 if (m_isConnected) {
313 WTFLogAlways("Error receiving IPC message on socket %d in process %d: %s", m_socketDescriptor, getpid(), strerror(errno));
314 connectionDidClose();
315 }
316 return;
317 }
318
319 if (!bytesRead) {
320 connectionDidClose();
321 return;
322 }
323
324 // Process messages from data received.
325 while (true) {
326 if (!processMessage())
327 break;
328 }
329 }
330}
331
332bool Connection::open()
333{
334 int flags = fcntl(m_socketDescriptor, F_GETFL, 0);
335 while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
336 if (errno != EINTR) {
337 ASSERT_NOT_REACHED();
338 return false;
339 }
340 }
341
342 RefPtr<Connection> protectedThis(this);
343 m_isConnected = true;
344#if USE(GLIB)
345 m_readSocketMonitor.start(m_socket.get(), G_IO_IN, m_connectionQueue->runLoop(), [protectedThis] (GIOCondition condition) -> gboolean {
346 if (condition & G_IO_HUP || condition & G_IO_ERR || condition & G_IO_NVAL) {
347 protectedThis->connectionDidClose();
348 return G_SOURCE_REMOVE;
349 }
350
351 if (condition & G_IO_IN) {
352 protectedThis->readyReadHandler();
353 return G_SOURCE_CONTINUE;
354 }
355
356 ASSERT_NOT_REACHED();
357 return G_SOURCE_REMOVE;
358 });
359#endif
360
361 // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal handler.
362 m_connectionQueue->dispatch([protectedThis] {
363 protectedThis->readyReadHandler();
364 });
365
366 return true;
367}
368
369bool Connection::platformCanSendOutgoingMessages() const
370{
371 return !m_pendingOutputMessage;
372}
373
374bool Connection::sendOutgoingMessage(std::unique_ptr<Encoder> encoder)
375{
376 COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
377
378 UnixMessage outputMessage(*encoder);
379 if (outputMessage.attachments().size() > (attachmentMaxAmount - 1)) {
380 ASSERT_NOT_REACHED();
381 return false;
382 }
383
384 size_t messageSizeWithBodyInline = sizeof(MessageInfo) + (outputMessage.attachments().size() * sizeof(AttachmentInfo)) + outputMessage.bodySize();
385 if (messageSizeWithBodyInline > messageMaxSize && outputMessage.bodySize()) {
386 RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::allocate(encoder->bufferSize());
387 if (!oolMessageBody)
388 return false;
389
390 WebKit::SharedMemory::Handle handle;
391 if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::Protection::ReadOnly))
392 return false;
393
394 outputMessage.messageInfo().setBodyOutOfLine();
395
396 memcpy(oolMessageBody->data(), outputMessage.body(), outputMessage.bodySize());
397
398 outputMessage.appendAttachment(handle.releaseAttachment());
399 }
400
401 return sendOutputMessage(outputMessage);
402}
403
404bool Connection::sendOutputMessage(UnixMessage& outputMessage)
405{
406 ASSERT(!m_pendingOutputMessage);
407
408 auto& messageInfo = outputMessage.messageInfo();
409 struct msghdr message;
410 memset(&message, 0, sizeof(message));
411
412 struct iovec iov[3];
413 memset(&iov, 0, sizeof(iov));
414
415 message.msg_iov = iov;
416 int iovLength = 1;
417
418 iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
419 iov[0].iov_len = sizeof(messageInfo);
420
421 Vector<AttachmentInfo> attachmentInfo;
422 MallocPtr<char> attachmentFDBuffer;
423
424 auto& attachments = outputMessage.attachments();
425 if (!attachments.isEmpty()) {
426 int* fdPtr = 0;
427
428 size_t attachmentFDBufferLength = std::count_if(attachments.begin(), attachments.end(),
429 [](const Attachment& attachment) {
430 return attachment.fileDescriptor() != -1;
431 });
432
433 if (attachmentFDBufferLength) {
434 attachmentFDBuffer = MallocPtr<char>::malloc(sizeof(char) * CMSG_SPACE(sizeof(int) * attachmentFDBufferLength));
435
436 message.msg_control = attachmentFDBuffer.get();
437 message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength);
438 memset(message.msg_control, 0, message.msg_controllen);
439
440 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
441 cmsg->cmsg_level = SOL_SOCKET;
442 cmsg->cmsg_type = SCM_RIGHTS;
443 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength);
444
445 fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
446 }
447
448 attachmentInfo.resize(attachments.size());
449 int fdIndex = 0;
450 for (size_t i = 0; i < attachments.size(); ++i) {
451 attachmentInfo[i].setType(attachments[i].type());
452
453 switch (attachments[i].type()) {
454 case Attachment::MappedMemoryType:
455 attachmentInfo[i].setSize(attachments[i].size());
456 FALLTHROUGH;
457 case Attachment::SocketType:
458 if (attachments[i].fileDescriptor() != -1) {
459 ASSERT(fdPtr);
460 fdPtr[fdIndex++] = attachments[i].fileDescriptor();
461 } else
462 attachmentInfo[i].setNull();
463 break;
464 case Attachment::Uninitialized:
465 default:
466 break;
467 }
468 }
469
470 iov[iovLength].iov_base = attachmentInfo.data();
471 iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size();
472 ++iovLength;
473 }
474
475 if (!messageInfo.isBodyOutOfLine() && outputMessage.bodySize()) {
476 iov[iovLength].iov_base = reinterpret_cast<void*>(outputMessage.body());
477 iov[iovLength].iov_len = outputMessage.bodySize();
478 ++iovLength;
479 }
480
481 message.msg_iovlen = iovLength;
482
483 while (sendmsg(m_socketDescriptor, &message, 0) == -1) {
484 if (errno == EINTR)
485 continue;
486 if (errno == EAGAIN || errno == EWOULDBLOCK) {
487#if USE(GLIB)
488 m_pendingOutputMessage = std::make_unique<UnixMessage>(WTFMove(outputMessage));
489 m_writeSocketMonitor.start(m_socket.get(), G_IO_OUT, m_connectionQueue->runLoop(), [this, protectedThis = makeRef(*this)] (GIOCondition condition) -> gboolean {
490 if (condition & G_IO_OUT) {
491 ASSERT(m_pendingOutputMessage);
492 // We can't stop the monitor from this lambda, because stop destroys the lambda.
493 m_connectionQueue->dispatch([this, protectedThis = makeRef(*this)] {
494 m_writeSocketMonitor.stop();
495 auto message = WTFMove(m_pendingOutputMessage);
496 if (m_isConnected) {
497 sendOutputMessage(*message);
498 sendOutgoingMessages();
499 }
500 });
501 }
502 return G_SOURCE_REMOVE;
503 });
504 return false;
505#else
506 struct pollfd pollfd;
507
508 pollfd.fd = m_socketDescriptor;
509 pollfd.events = POLLOUT;
510 pollfd.revents = 0;
511 poll(&pollfd, 1, -1);
512 continue;
513#endif
514 }
515
516 if (m_isConnected)
517 WTFLogAlways("Error sending IPC message: %s", strerror(errno));
518 return false;
519 }
520 return true;
521}
522
523Connection::SocketPair Connection::createPlatformConnection(unsigned options)
524{
525 int sockets[2];
526 RELEASE_ASSERT(socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) != -1);
527
528 if (options & SetCloexecOnServer) {
529 // Don't expose the child socket to the parent process.
530 if (!setCloseOnExec(sockets[1]))
531 RELEASE_ASSERT_NOT_REACHED();
532 }
533
534 if (options & SetCloexecOnClient) {
535 // Don't expose the parent socket to potential future children.
536 if (!setCloseOnExec(sockets[0]))
537 RELEASE_ASSERT_NOT_REACHED();
538 }
539
540 SocketPair socketPair = { sockets[0], sockets[1] };
541 return socketPair;
542}
543
544void Connection::willSendSyncMessage(OptionSet<SendSyncOption>)
545{
546}
547
548void Connection::didReceiveSyncReply(OptionSet<SendSyncOption>)
549{
550}
551
552} // namespace IPC
553