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 if (!setNonBlock(m_socketDescriptor)) {
335 ASSERT_NOT_REACHED();
336 return false;
337 }
338
339 RefPtr<Connection> protectedThis(this);
340 m_isConnected = true;
341#if USE(GLIB)
342 m_readSocketMonitor.start(m_socket.get(), G_IO_IN, m_connectionQueue->runLoop(), [protectedThis] (GIOCondition condition) -> gboolean {
343 if (condition & G_IO_HUP || condition & G_IO_ERR || condition & G_IO_NVAL) {
344 protectedThis->connectionDidClose();
345 return G_SOURCE_REMOVE;
346 }
347
348 if (condition & G_IO_IN) {
349 protectedThis->readyReadHandler();
350 return G_SOURCE_CONTINUE;
351 }
352
353 ASSERT_NOT_REACHED();
354 return G_SOURCE_REMOVE;
355 });
356#endif
357
358 // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal handler.
359 m_connectionQueue->dispatch([protectedThis] {
360 protectedThis->readyReadHandler();
361 });
362
363 return true;
364}
365
366bool Connection::platformCanSendOutgoingMessages() const
367{
368 return !m_pendingOutputMessage;
369}
370
371bool Connection::sendOutgoingMessage(std::unique_ptr<Encoder> encoder)
372{
373 COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
374
375 UnixMessage outputMessage(*encoder);
376 if (outputMessage.attachments().size() > (attachmentMaxAmount - 1)) {
377 ASSERT_NOT_REACHED();
378 return false;
379 }
380
381 size_t messageSizeWithBodyInline = sizeof(MessageInfo) + (outputMessage.attachments().size() * sizeof(AttachmentInfo)) + outputMessage.bodySize();
382 if (messageSizeWithBodyInline > messageMaxSize && outputMessage.bodySize()) {
383 RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::allocate(encoder->bufferSize());
384 if (!oolMessageBody)
385 return false;
386
387 WebKit::SharedMemory::Handle handle;
388 if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::Protection::ReadOnly))
389 return false;
390
391 outputMessage.messageInfo().setBodyOutOfLine();
392
393 memcpy(oolMessageBody->data(), outputMessage.body(), outputMessage.bodySize());
394
395 outputMessage.appendAttachment(handle.releaseAttachment());
396 }
397
398 return sendOutputMessage(outputMessage);
399}
400
401bool Connection::sendOutputMessage(UnixMessage& outputMessage)
402{
403 ASSERT(!m_pendingOutputMessage);
404
405 auto& messageInfo = outputMessage.messageInfo();
406 struct msghdr message;
407 memset(&message, 0, sizeof(message));
408
409 struct iovec iov[3];
410 memset(&iov, 0, sizeof(iov));
411
412 message.msg_iov = iov;
413 int iovLength = 1;
414
415 iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
416 iov[0].iov_len = sizeof(messageInfo);
417
418 Vector<AttachmentInfo> attachmentInfo;
419 MallocPtr<char> attachmentFDBuffer;
420
421 auto& attachments = outputMessage.attachments();
422 if (!attachments.isEmpty()) {
423 int* fdPtr = 0;
424
425 size_t attachmentFDBufferLength = std::count_if(attachments.begin(), attachments.end(),
426 [](const Attachment& attachment) {
427 return attachment.fileDescriptor() != -1;
428 });
429
430 if (attachmentFDBufferLength) {
431 attachmentFDBuffer = MallocPtr<char>::malloc(sizeof(char) * CMSG_SPACE(sizeof(int) * attachmentFDBufferLength));
432
433 message.msg_control = attachmentFDBuffer.get();
434 message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength);
435 memset(message.msg_control, 0, message.msg_controllen);
436
437 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
438 cmsg->cmsg_level = SOL_SOCKET;
439 cmsg->cmsg_type = SCM_RIGHTS;
440 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength);
441
442 fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
443 }
444
445 attachmentInfo.resize(attachments.size());
446 int fdIndex = 0;
447 for (size_t i = 0; i < attachments.size(); ++i) {
448 attachmentInfo[i].setType(attachments[i].type());
449
450 switch (attachments[i].type()) {
451 case Attachment::MappedMemoryType:
452 attachmentInfo[i].setSize(attachments[i].size());
453 FALLTHROUGH;
454 case Attachment::SocketType:
455 if (attachments[i].fileDescriptor() != -1) {
456 ASSERT(fdPtr);
457 fdPtr[fdIndex++] = attachments[i].fileDescriptor();
458 } else
459 attachmentInfo[i].setNull();
460 break;
461 case Attachment::Uninitialized:
462 default:
463 break;
464 }
465 }
466
467 iov[iovLength].iov_base = attachmentInfo.data();
468 iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size();
469 ++iovLength;
470 }
471
472 if (!messageInfo.isBodyOutOfLine() && outputMessage.bodySize()) {
473 iov[iovLength].iov_base = reinterpret_cast<void*>(outputMessage.body());
474 iov[iovLength].iov_len = outputMessage.bodySize();
475 ++iovLength;
476 }
477
478 message.msg_iovlen = iovLength;
479
480 while (sendmsg(m_socketDescriptor, &message, 0) == -1) {
481 if (errno == EINTR)
482 continue;
483 if (errno == EAGAIN || errno == EWOULDBLOCK) {
484#if USE(GLIB)
485 m_pendingOutputMessage = std::make_unique<UnixMessage>(WTFMove(outputMessage));
486 m_writeSocketMonitor.start(m_socket.get(), G_IO_OUT, m_connectionQueue->runLoop(), [this, protectedThis = makeRef(*this)] (GIOCondition condition) -> gboolean {
487 if (condition & G_IO_OUT) {
488 ASSERT(m_pendingOutputMessage);
489 // We can't stop the monitor from this lambda, because stop destroys the lambda.
490 m_connectionQueue->dispatch([this, protectedThis = makeRef(*this)] {
491 m_writeSocketMonitor.stop();
492 auto message = WTFMove(m_pendingOutputMessage);
493 if (m_isConnected) {
494 sendOutputMessage(*message);
495 sendOutgoingMessages();
496 }
497 });
498 }
499 return G_SOURCE_REMOVE;
500 });
501 return false;
502#else
503 struct pollfd pollfd;
504
505 pollfd.fd = m_socketDescriptor;
506 pollfd.events = POLLOUT;
507 pollfd.revents = 0;
508 poll(&pollfd, 1, -1);
509 continue;
510#endif
511 }
512
513 if (m_isConnected)
514 WTFLogAlways("Error sending IPC message: %s", strerror(errno));
515 return false;
516 }
517 return true;
518}
519
520Connection::SocketPair Connection::createPlatformConnection(unsigned options)
521{
522 int sockets[2];
523 RELEASE_ASSERT(socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) != -1);
524
525 if (options & SetCloexecOnServer) {
526 // Don't expose the child socket to the parent process.
527 if (!setCloseOnExec(sockets[1]))
528 RELEASE_ASSERT_NOT_REACHED();
529 }
530
531 if (options & SetCloexecOnClient) {
532 // Don't expose the parent socket to potential future children.
533 if (!setCloseOnExec(sockets[0]))
534 RELEASE_ASSERT_NOT_REACHED();
535 }
536
537 SocketPair socketPair = { sockets[0], sockets[1] };
538 return socketPair;
539}
540
541void Connection::willSendSyncMessage(OptionSet<SendSyncOption>)
542{
543}
544
545void Connection::didReceiveSyncReply(OptionSet<SendSyncOption>)
546{
547}
548
549} // namespace IPC
550