WTF::Function does not allow for reference / non-default constructible return types
[WebKit-https.git] / Source / JavaScriptCore / runtime / ArrayBuffer.cpp
1 /*
2  * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ArrayBuffer.h"
28
29 #include "ArrayBufferNeuteringWatchpoint.h"
30 #include "JSArrayBufferView.h"
31 #include "JSCInlines.h"
32 #include <wtf/Gigacage.h>
33
34 namespace JSC {
35
36 SharedArrayBufferContents::SharedArrayBufferContents(void* data, ArrayBufferDestructorFunction&& destructor)
37     : m_data(data)
38     , m_destructor(WTFMove(destructor))
39 {
40 }
41
42 SharedArrayBufferContents::~SharedArrayBufferContents()
43 {
44     m_destructor(m_data);
45 }
46
47 ArrayBufferContents::ArrayBufferContents()
48 {
49     reset();
50 }
51
52 ArrayBufferContents::ArrayBufferContents(ArrayBufferContents&& other)
53 {
54     reset();
55     other.transferTo(*this);
56 }
57
58 ArrayBufferContents::ArrayBufferContents(void* data, unsigned sizeInBytes, ArrayBufferDestructorFunction&& destructor)
59     : m_data(data)
60     , m_sizeInBytes(sizeInBytes)
61 {
62     m_destructor = WTFMove(destructor);
63 }
64
65 ArrayBufferContents& ArrayBufferContents::operator=(ArrayBufferContents&& other)
66 {
67     other.transferTo(*this);
68     return *this;
69 }
70
71 ArrayBufferContents::~ArrayBufferContents()
72 {
73     destroy();
74 }
75
76 void ArrayBufferContents::clear()
77 {
78     destroy();
79     reset();
80 }
81
82 void ArrayBufferContents::destroy()
83 {
84     m_destructor(m_data);
85 }
86
87 void ArrayBufferContents::reset()
88 {
89     m_destructor = [] (void*) { };
90     m_shared = nullptr;
91     m_data = nullptr;
92     m_sizeInBytes = 0;
93 }
94
95 void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByteSize, InitializationPolicy policy)
96 {
97     // Do not allow 31-bit overflow of the total size.
98     if (numElements) {
99         unsigned totalSize = numElements * elementByteSize;
100         if (totalSize / numElements != elementByteSize
101             || totalSize > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) {
102             reset();
103             return;
104         }
105     }
106     size_t size = static_cast<size_t>(numElements) * static_cast<size_t>(elementByteSize);
107     if (!size)
108         size = 1; // Make sure malloc actually allocates something, but not too much. We use null to mean that the buffer is neutered.
109     m_data = Gigacage::tryMalloc(Gigacage::Primitive, size);
110     if (!m_data) {
111         reset();
112         return;
113     }
114     
115     if (policy == ZeroInitialize)
116         memset(m_data, 0, size);
117
118     m_sizeInBytes = numElements * elementByteSize;
119     m_destructor = [] (void* p) { Gigacage::free(Gigacage::Primitive, p); };
120 }
121
122 void ArrayBufferContents::makeShared()
123 {
124     m_shared = adoptRef(new SharedArrayBufferContents(m_data, WTFMove(m_destructor)));
125     m_destructor = [] (void*) { };
126 }
127
128 void ArrayBufferContents::transferTo(ArrayBufferContents& other)
129 {
130     other.clear();
131     other.m_data = m_data;
132     other.m_sizeInBytes = m_sizeInBytes;
133     other.m_destructor = WTFMove(m_destructor);
134     other.m_shared = m_shared;
135     reset();
136 }
137
138 void ArrayBufferContents::copyTo(ArrayBufferContents& other)
139 {
140     ASSERT(!other.m_data);
141     other.tryAllocate(m_sizeInBytes, sizeof(char), ArrayBufferContents::DontInitialize);
142     if (!other.m_data)
143         return;
144     memcpy(other.m_data, m_data, m_sizeInBytes);
145     other.m_sizeInBytes = m_sizeInBytes;
146 }
147
148 void ArrayBufferContents::shareWith(ArrayBufferContents& other)
149 {
150     ASSERT(!other.m_data);
151     ASSERT(m_shared);
152     other.m_destructor = [] (void*) { };
153     other.m_shared = m_shared;
154     other.m_data = m_data;
155     other.m_sizeInBytes = m_sizeInBytes;
156 }
157
158 Ref<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize)
159 {
160     auto buffer = tryCreate(numElements, elementByteSize);
161     if (!buffer)
162         CRASH();
163     return buffer.releaseNonNull();
164 }
165
166 Ref<ArrayBuffer> ArrayBuffer::create(ArrayBuffer& other)
167 {
168     return ArrayBuffer::create(other.data(), other.byteLength());
169 }
170
171 Ref<ArrayBuffer> ArrayBuffer::create(const void* source, unsigned byteLength)
172 {
173     auto buffer = tryCreate(source, byteLength);
174     if (!buffer)
175         CRASH();
176     return buffer.releaseNonNull();
177 }
178
179 Ref<ArrayBuffer> ArrayBuffer::create(ArrayBufferContents&& contents)
180 {
181     return adoptRef(*new ArrayBuffer(WTFMove(contents)));
182 }
183
184 // FIXME: We cannot use this except if the memory comes from the cage.
185 // Current this is only used from:
186 // - JSGenericTypedArrayView<>::slowDownAndWasteMemory. But in that case, the memory should have already come
187 //   from the cage.
188 Ref<ArrayBuffer> ArrayBuffer::createAdopted(const void* data, unsigned byteLength)
189 {
190     return createFromBytes(data, byteLength, [] (void* p) { Gigacage::free(Gigacage::Primitive, p); });
191 }
192
193 // FIXME: We cannot use this except if the memory comes from the cage.
194 // Currently this is only used from:
195 // - The C API. We could support that by either having the system switch to a mode where typed arrays are no
196 //   longer caged, or we could introduce a new set of typed array types that are uncaged and get accessed
197 //   differently.
198 // - WebAssembly. Wasm should allocate from the cage.
199 Ref<ArrayBuffer> ArrayBuffer::createFromBytes(const void* data, unsigned byteLength, ArrayBufferDestructorFunction&& destructor)
200 {
201     if (data && byteLength && !Gigacage::isCaged(Gigacage::Primitive, data))
202         Gigacage::disablePrimitiveGigacage();
203     
204     ArrayBufferContents contents(const_cast<void*>(data), byteLength, WTFMove(destructor));
205     return create(WTFMove(contents));
206 }
207
208 RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(unsigned numElements, unsigned elementByteSize)
209 {
210     return tryCreate(numElements, elementByteSize, ArrayBufferContents::ZeroInitialize);
211 }
212
213 RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(ArrayBuffer& other)
214 {
215     return tryCreate(other.data(), other.byteLength());
216 }
217
218 RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(const void* source, unsigned byteLength)
219 {
220     ArrayBufferContents contents;
221     contents.tryAllocate(byteLength, 1, ArrayBufferContents::DontInitialize);
222     if (!contents.m_data)
223         return nullptr;
224     return createInternal(WTFMove(contents), source, byteLength);
225 }
226
227 Ref<ArrayBuffer> ArrayBuffer::createUninitialized(unsigned numElements, unsigned elementByteSize)
228 {
229     return create(numElements, elementByteSize, ArrayBufferContents::DontInitialize);
230 }
231
232 RefPtr<ArrayBuffer> ArrayBuffer::tryCreateUninitialized(unsigned numElements, unsigned elementByteSize)
233 {
234     return tryCreate(numElements, elementByteSize, ArrayBufferContents::DontInitialize);
235 }
236
237 Ref<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy)
238 {
239     auto buffer = tryCreate(numElements, elementByteSize, policy);
240     if (!buffer)
241         CRASH();
242     return buffer.releaseNonNull();
243 }
244
245 Ref<ArrayBuffer> ArrayBuffer::createInternal(ArrayBufferContents&& contents, const void* source, unsigned byteLength)
246 {
247     ASSERT(!byteLength || source);
248     auto buffer = adoptRef(*new ArrayBuffer(WTFMove(contents)));
249     memcpy(buffer->data(), source, byteLength);
250     return buffer;
251 }
252
253 RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy)
254 {
255     ArrayBufferContents contents;
256     contents.tryAllocate(numElements, elementByteSize, policy);
257     if (!contents.m_data)
258         return nullptr;
259     return adoptRef(*new ArrayBuffer(WTFMove(contents)));
260 }
261
262 ArrayBuffer::ArrayBuffer(ArrayBufferContents&& contents)
263     : m_contents(WTFMove(contents))
264     , m_pinCount(0)
265     , m_isWasmMemory(false)
266     , m_locked(false)
267 {
268 }
269
270 RefPtr<ArrayBuffer> ArrayBuffer::slice(int begin, int end) const
271 {
272     return sliceImpl(clampIndex(begin), clampIndex(end));
273 }
274
275 RefPtr<ArrayBuffer> ArrayBuffer::slice(int begin) const
276 {
277     return sliceImpl(clampIndex(begin), byteLength());
278 }
279
280 RefPtr<ArrayBuffer> ArrayBuffer::sliceImpl(unsigned begin, unsigned end) const
281 {
282     unsigned size = begin <= end ? end - begin : 0;
283     RefPtr<ArrayBuffer> result = ArrayBuffer::create(static_cast<const char*>(data()) + begin, size);
284     result->setSharingMode(sharingMode());
285     return result;
286 }
287
288 void ArrayBuffer::makeShared()
289 {
290     m_contents.makeShared();
291     m_locked = true;
292 }
293
294 void ArrayBuffer::makeWasmMemory()
295 {
296     m_locked = true;
297     m_isWasmMemory = true;
298 }
299
300 void ArrayBuffer::setSharingMode(ArrayBufferSharingMode newSharingMode)
301 {
302     if (newSharingMode == sharingMode())
303         return;
304     RELEASE_ASSERT(!isShared()); // Cannot revert sharing.
305     RELEASE_ASSERT(newSharingMode == ArrayBufferSharingMode::Shared);
306     makeShared();
307 }
308
309 bool ArrayBuffer::shareWith(ArrayBufferContents& result)
310 {
311     if (!m_contents.m_data || !isShared()) {
312         result.m_data = nullptr;
313         return false;
314     }
315     
316     m_contents.shareWith(result);
317     return true;
318 }
319
320 bool ArrayBuffer::transferTo(VM& vm, ArrayBufferContents& result)
321 {
322     Ref<ArrayBuffer> protect(*this);
323
324     if (!m_contents.m_data) {
325         result.m_data = 0;
326         return false;
327     }
328     
329     if (isShared()) {
330         m_contents.shareWith(result);
331         return true;
332     }
333
334     bool isNeuterable = !m_pinCount && !m_locked;
335
336     if (!isNeuterable) {
337         m_contents.copyTo(result);
338         if (!result.m_data)
339             return false;
340         return true;
341     }
342
343     m_contents.transferTo(result);
344     notifyIncommingReferencesOfTransfer(vm);
345     return true;
346 }
347
348 // We allow neutering wasm memory ArrayBuffers even though they are locked.
349 void ArrayBuffer::neuter(VM& vm)
350 {
351     ASSERT(isWasmMemory());
352     ArrayBufferContents unused;
353     m_contents.transferTo(unused);
354     notifyIncommingReferencesOfTransfer(vm);
355 }
356
357 void ArrayBuffer::notifyIncommingReferencesOfTransfer(VM& vm)
358 {
359     for (size_t i = numberOfIncomingReferences(); i--;) {
360         JSCell* cell = incomingReferenceAt(i);
361         if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(vm, cell))
362             view->neuter();
363         else if (ArrayBufferNeuteringWatchpoint* watchpoint = jsDynamicCast<ArrayBufferNeuteringWatchpoint*>(vm, cell))
364             watchpoint->fireAll();
365     }
366 }
367
368 ASCIILiteral errorMesasgeForTransfer(ArrayBuffer* buffer)
369 {
370     ASSERT(buffer->isLocked());
371     if (buffer->isShared())
372         return ASCIILiteral("Cannot transfer a SharedArrayBuffer");
373     if (buffer->isWasmMemory())
374         return ASCIILiteral("Cannot transfer a WebAssembly.Memory");
375     return ASCIILiteral("Cannot transfer an ArrayBuffer whose backing store has been accessed by the JavaScriptCore C API");
376 }
377
378 } // namespace JSC
379