[Linux] Use memfd_create when available in SharedMemory implementation
[WebKit-https.git] / Source / WebKit / Platform / unix / SharedMemoryUnix.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (c) 2010 University of Szeged
4  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
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 #if USE(UNIX_DOMAIN_SOCKETS)
30 #include "SharedMemory.h"
31
32 #include "Decoder.h"
33 #include "Encoder.h"
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <sys/mman.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/RandomNumber.h>
43 #include <wtf/UniStdExtras.h>
44 #include <wtf/text/CString.h>
45 #include <wtf/text/WTFString.h>
46
47 #if HAVE(LINUX_MEMFD_H)
48 #include <linux/memfd.h>
49 #include <sys/syscall.h>
50 #endif
51
52 namespace WebKit {
53
54 SharedMemory::Handle::Handle()
55 {
56 }
57
58 SharedMemory::Handle::~Handle()
59 {
60 }
61
62 void SharedMemory::Handle::clear()
63 {
64     m_attachment = IPC::Attachment();
65 }
66
67 bool SharedMemory::Handle::isNull() const
68 {
69     return m_attachment.fileDescriptor() == -1;
70 }
71
72 void SharedMemory::Handle::encode(IPC::Encoder& encoder) const
73 {
74     encoder << releaseAttachment();
75 }
76
77 bool SharedMemory::Handle::decode(IPC::Decoder& decoder, Handle& handle)
78 {
79     ASSERT_ARG(handle, handle.isNull());
80
81     IPC::Attachment attachment;
82     if (!decoder.decode(attachment))
83         return false;
84
85     handle.adoptAttachment(WTFMove(attachment));
86     return true;
87 }
88
89 IPC::Attachment SharedMemory::Handle::releaseAttachment() const
90 {
91     return WTFMove(m_attachment);
92 }
93
94 void SharedMemory::Handle::adoptAttachment(IPC::Attachment&& attachment)
95 {
96     ASSERT(isNull());
97
98     m_attachment = WTFMove(attachment);
99 }
100
101 static inline int accessModeMMap(SharedMemory::Protection protection)
102 {
103     switch (protection) {
104     case SharedMemory::Protection::ReadOnly:
105         return PROT_READ;
106     case SharedMemory::Protection::ReadWrite:
107         return PROT_READ | PROT_WRITE;
108     }
109
110     ASSERT_NOT_REACHED();
111     return PROT_READ | PROT_WRITE;
112 }
113
114 static int createSharedMemory()
115 {
116 #if HAVE(LINUX_MEMFD_H)
117     static bool isMemFdAvailable = true;
118     int fileDescriptor = -1;
119     if (isMemFdAvailable) {
120         do {
121             fileDescriptor = syscall(__NR_memfd_create, "WebKitSharedMemory", MFD_CLOEXEC);
122         } while (fileDescriptor == -1 && errno == EINTR);
123
124         if (fileDescriptor != -1)
125             return fileDescriptor;
126
127         if (errno != ENOSYS)
128             return fileDescriptor;
129
130         isMemFdAvailable = false;
131     }
132 #endif
133
134     CString tempName;
135     for (int tries = 0; fileDescriptor == -1 && tries < 10; ++tries) {
136         String name = String("/WK2SharedMemory.") + String::number(static_cast<unsigned>(WTF::randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)));
137         tempName = name.utf8();
138
139         do {
140             fileDescriptor = shm_open(tempName.data(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
141         } while (fileDescriptor == -1 && errno == EINTR);
142     }
143
144     if (fileDescriptor != -1)
145         shm_unlink(tempName.data());
146
147     return fileDescriptor;
148 }
149
150 RefPtr<SharedMemory> SharedMemory::create(void* address, size_t size, Protection protection)
151 {
152     int fileDescriptor = createSharedMemory();
153     if (fileDescriptor == -1) {
154         WTFLogAlways("Failed to create shared memory: %s", strerror(errno));
155         return nullptr;
156     }
157
158     while (ftruncate(fileDescriptor, size) == -1) {
159         if (errno != EINTR) {
160             closeWithRetry(fileDescriptor);
161             return nullptr;
162         }
163     }
164
165     void* data = mmap(address, size, accessModeMMap(protection), MAP_SHARED, fileDescriptor, 0);
166     if (data == MAP_FAILED) {
167         closeWithRetry(fileDescriptor);
168         return nullptr;
169     }
170
171     RefPtr<SharedMemory> instance = adoptRef(new SharedMemory());
172     instance->m_data = data;
173     instance->m_fileDescriptor = fileDescriptor;
174     instance->m_size = size;
175     return instance;
176 }
177
178 RefPtr<SharedMemory> SharedMemory::allocate(size_t size)
179 {
180     return SharedMemory::create(nullptr, size, SharedMemory::Protection::ReadWrite);
181 }
182
183 RefPtr<SharedMemory> SharedMemory::map(const Handle& handle, Protection protection)
184 {
185     ASSERT(!handle.isNull());
186
187     int fd = handle.m_attachment.releaseFileDescriptor();
188     void* data = mmap(0, handle.m_attachment.size(), accessModeMMap(protection), MAP_SHARED, fd, 0);
189     closeWithRetry(fd);
190     if (data == MAP_FAILED)
191         return nullptr;
192
193     RefPtr<SharedMemory> instance = wrapMap(data, handle.m_attachment.size(), -1);
194     instance->m_fileDescriptor = std::nullopt;
195     instance->m_isWrappingMap = false;
196     return instance;
197 }
198
199 RefPtr<SharedMemory> SharedMemory::wrapMap(void* data, size_t size, int fileDescriptor)
200 {
201     RefPtr<SharedMemory> instance = adoptRef(new SharedMemory());
202     instance->m_data = data;
203     instance->m_size = size;
204     instance->m_fileDescriptor = fileDescriptor;
205     instance->m_isWrappingMap = true;
206     return instance;
207 }
208
209 SharedMemory::~SharedMemory()
210 {
211     if (m_isWrappingMap)
212         return;
213
214     munmap(m_data, m_size);
215     if (m_fileDescriptor)
216         closeWithRetry(m_fileDescriptor.value());
217 }
218
219 bool SharedMemory::createHandle(Handle& handle, Protection)
220 {
221     ASSERT_ARG(handle, handle.isNull());
222     ASSERT(m_fileDescriptor);
223
224     // FIXME: Handle the case where the passed Protection is ReadOnly.
225     // See https://bugs.webkit.org/show_bug.cgi?id=131542.
226
227     int duplicatedHandle = dupCloseOnExec(m_fileDescriptor.value());
228     if (duplicatedHandle == -1) {
229         ASSERT_NOT_REACHED();
230         return false;
231     }
232     handle.m_attachment = IPC::Attachment(duplicatedHandle, m_size);
233     return true;
234 }
235
236 unsigned SharedMemory::systemPageSize()
237 {
238     static unsigned pageSize = 0;
239
240     if (!pageSize)
241         pageSize = getpagesize();
242
243     return pageSize;
244 }
245
246 } // namespace WebKit
247
248 #endif
249