Sharing SharedBuffer between WebCore and ImageIO is racy and crash prone
[WebKit-https.git] / Source / WebCore / platform / SharedBuffer.cpp
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "SharedBuffer.h"
29
30 #include "PurgeableBuffer.h"
31 #include <wtf/PassOwnPtr.h>
32 #include <wtf/unicode/UTF8.h>
33
34 #if ENABLE(DISK_IMAGE_CACHE)
35 #include "DiskImageCacheIOS.h"
36 #endif
37
38 namespace WebCore {
39
40 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
41 static const unsigned segmentSize = 0x1000;
42 static const unsigned segmentPositionMask = 0x0FFF;
43
44 static inline unsigned segmentIndex(unsigned position)
45 {
46     return position / segmentSize;
47 }
48
49 static inline unsigned offsetInSegment(unsigned position)
50 {
51     return position & segmentPositionMask;
52 }
53
54 static inline char* allocateSegment() WARN_UNUSED_RETURN;
55 static inline char* allocateSegment()
56 {
57     return static_cast<char*>(fastMalloc(segmentSize));
58 }
59
60 static inline void freeSegment(char* p)
61 {
62     fastFree(p);
63 }
64 #endif
65
66 SharedBuffer::SharedBuffer()
67     : m_size(0)
68     , m_buffer(adoptRef(new DataBuffer))
69     , m_shouldUsePurgeableMemory(false)
70 #if ENABLE(DISK_IMAGE_CACHE)
71     , m_isMemoryMapped(false)
72     , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
73     , m_notifyMemoryMappedCallback(nullptr)
74     , m_notifyMemoryMappedCallbackData(nullptr)
75 #endif
76 {
77 }
78
79 SharedBuffer::SharedBuffer(unsigned size)
80     : m_size(size)
81     , m_buffer(adoptRef(new DataBuffer))
82     , m_shouldUsePurgeableMemory(false)
83 #if ENABLE(DISK_IMAGE_CACHE)
84     , m_isMemoryMapped(false)
85     , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
86     , m_notifyMemoryMappedCallback(nullptr)
87     , m_notifyMemoryMappedCallbackData(nullptr)
88 #endif
89 {
90 }
91
92 SharedBuffer::SharedBuffer(const char* data, unsigned size)
93     : m_size(0)
94     , m_buffer(adoptRef(new DataBuffer))
95     , m_shouldUsePurgeableMemory(false)
96 #if ENABLE(DISK_IMAGE_CACHE)
97     , m_isMemoryMapped(false)
98     , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
99     , m_notifyMemoryMappedCallback(nullptr)
100     , m_notifyMemoryMappedCallbackData(nullptr)
101 #endif
102 {
103     append(data, size);
104 }
105
106 SharedBuffer::SharedBuffer(const unsigned char* data, unsigned size)
107     : m_size(0)
108     , m_buffer(adoptRef(new DataBuffer))
109     , m_shouldUsePurgeableMemory(false)
110 #if ENABLE(DISK_IMAGE_CACHE)
111     , m_isMemoryMapped(false)
112     , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
113     , m_notifyMemoryMappedCallback(nullptr)
114     , m_notifyMemoryMappedCallbackData(nullptr)
115 #endif
116 {
117     append(reinterpret_cast<const char*>(data), size);
118 }
119     
120 SharedBuffer::~SharedBuffer()
121 {
122 #if ENABLE(DISK_IMAGE_CACHE)
123     if (m_diskImageCacheId) {
124         diskImageCache().removeItem(m_diskImageCacheId);
125         m_isMemoryMapped = false;
126         m_diskImageCacheId = DiskImageCache::invalidDiskCacheId;
127     }
128 #endif
129     clear();
130 }
131
132 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
133 {
134     RefPtr<SharedBuffer> buffer = create();
135     buffer->m_buffer->data.swap(vector);
136     buffer->m_size = buffer->m_buffer->data.size();
137     return buffer.release();
138 }
139
140 PassRefPtr<SharedBuffer> SharedBuffer::adoptPurgeableBuffer(PassOwnPtr<PurgeableBuffer> purgeableBuffer) 
141
142     ASSERT(!purgeableBuffer->isPurgeable());
143     RefPtr<SharedBuffer> buffer = create();
144     buffer->m_purgeableBuffer = purgeableBuffer;
145     return buffer.release();
146 }
147
148 unsigned SharedBuffer::size() const
149 {
150     if (hasPlatformData())
151         return platformDataSize();
152     
153     if (m_purgeableBuffer)
154         return m_purgeableBuffer->size();
155     
156     return m_size;
157 }
158
159 #if ENABLE(DISK_IMAGE_CACHE)
160 bool SharedBuffer::isAllowedToBeMemoryMapped() const
161 {
162     return m_diskImageCacheId != DiskImageCache::invalidDiskCacheId;
163 }
164
165 SharedBuffer::MemoryMappingState SharedBuffer::allowToBeMemoryMapped()
166 {
167     if (isMemoryMapped())
168         return SharedBuffer::SuccessAlreadyMapped;
169
170     if (isAllowedToBeMemoryMapped())
171         return SharedBuffer::PreviouslyQueuedForMapping;
172
173     m_diskImageCacheId = diskImageCache().writeItem(this);
174     if (m_diskImageCacheId == DiskImageCache::invalidDiskCacheId)
175         return SharedBuffer::FailureCacheFull;
176
177     return SharedBuffer::QueuedForMapping;
178 }
179
180 void SharedBuffer::failedMemoryMap()
181 {
182     if (m_notifyMemoryMappedCallback)
183         m_notifyMemoryMappedCallback(this, SharedBuffer::Failed, m_notifyMemoryMappedCallbackData);
184 }
185
186 void SharedBuffer::markAsMemoryMapped()
187 {
188     ASSERT(!isMemoryMapped());
189
190     m_isMemoryMapped = true;
191     unsigned savedSize = size();
192     clear();
193     m_size = savedSize;
194
195     if (m_notifyMemoryMappedCallback)
196         m_notifyMemoryMappedCallback(this, SharedBuffer::Succeeded, m_notifyMemoryMappedCallbackData);
197 }
198
199 SharedBuffer::MemoryMappedNotifyCallbackData SharedBuffer::memoryMappedNotificationCallbackData() const
200 {
201     return m_notifyMemoryMappedCallbackData;
202 }
203
204 SharedBuffer::MemoryMappedNotifyCallback SharedBuffer::memoryMappedNotificationCallback() const
205 {
206     return m_notifyMemoryMappedCallback;
207 }
208
209 void SharedBuffer::setMemoryMappedNotificationCallback(MemoryMappedNotifyCallback callback, MemoryMappedNotifyCallbackData data)
210 {
211     ASSERT(!m_notifyMemoryMappedCallback || !callback);
212     ASSERT(!m_notifyMemoryMappedCallbackData || !data);
213
214     m_notifyMemoryMappedCallback = callback;
215     m_notifyMemoryMappedCallbackData = data;
216 }
217 #endif
218
219 // Try to create a PurgeableBuffer. We can fail to create one for any of the
220 // following reasons:
221 //   - shouldUsePurgeableMemory is set to false.
222 //   - the size of the buffer is less than the minimum size required by
223 //     PurgeableBuffer (currently 16k).
224 //   - PurgeableBuffer::createUninitialized() call fails.
225 void SharedBuffer::createPurgeableBuffer() const
226 {
227     if (m_purgeableBuffer)
228         return;
229
230     if (hasPlatformData())
231         return;
232
233 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
234     if (singleDataArrayBuffer())
235         return;
236 #endif
237
238     if (!m_buffer->hasOneRef())
239         return;
240
241     if (!m_shouldUsePurgeableMemory)
242         return;
243
244     char* destination = 0;
245     m_purgeableBuffer = PurgeableBuffer::createUninitialized(m_size, destination);
246     if (!m_purgeableBuffer)
247         return;
248     unsigned bufferSize = m_buffer->data.size();
249     if (bufferSize) {
250         memcpy(destination, m_buffer->data.data(), bufferSize);
251         destination += bufferSize;
252         (const_cast<SharedBuffer*>(this))->clearDataBuffer();
253     }
254     copyBufferAndClear(destination, m_size - bufferSize);
255 }
256
257 const char* SharedBuffer::data() const
258 {
259 #if ENABLE(DISK_IMAGE_CACHE)
260     if (isMemoryMapped())
261         return static_cast<const char*>(diskImageCache().dataForItem(m_diskImageCacheId));
262 #endif
263
264     if (hasPlatformData())
265         return platformData();
266
267 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
268     if (const char* buffer = singleDataArrayBuffer())
269         return buffer;
270 #endif
271
272     createPurgeableBuffer();
273
274     if (m_purgeableBuffer)
275         return m_purgeableBuffer->data();
276     
277     return this->buffer().data();
278 }
279
280 PassRefPtr<ArrayBuffer> SharedBuffer::createArrayBuffer() const
281 {
282     RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), sizeof(char));
283
284     const char* segment = 0;
285     unsigned position = 0;
286     while (unsigned segmentSize = getSomeData(segment, position)) {
287         memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize);
288         position += segmentSize;
289     }
290
291     if (position != arrayBuffer->byteLength()) {
292         ASSERT_NOT_REACHED();
293         // Don't return the incomplete ArrayBuffer.
294         return 0;
295     }
296
297     return arrayBuffer.release();
298 }
299
300 void SharedBuffer::append(SharedBuffer* data)
301 {
302     if (maybeAppendPlatformData(data))
303         return;
304 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
305     if (maybeAppendDataArray(data))
306         return;
307 #endif
308
309     const char* segment;
310     size_t position = 0;
311     while (size_t length = data->getSomeData(segment, position)) {
312         append(segment, length);
313         position += length;
314     }
315 }
316
317 void SharedBuffer::append(const char* data, unsigned length)
318 {
319     ASSERT(!m_purgeableBuffer);
320 #if ENABLE(DISK_IMAGE_CACHE)
321     ASSERT(!isMemoryMapped());
322 #endif
323     if (!length)
324         return;
325
326     maybeTransferPlatformData();
327
328 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
329     unsigned positionInSegment = offsetInSegment(m_size - m_buffer->data.size());
330     m_size += length;
331
332     if (m_size <= segmentSize) {
333         // No need to use segments for small resource data
334         if (m_buffer->data.isEmpty())
335             m_buffer->data.reserveInitialCapacity(length);
336         appendToDataBuffer(data, length);
337         return;
338     }
339
340     char* segment;
341     if (!positionInSegment) {
342         segment = allocateSegment();
343         m_segments.append(segment);
344     } else
345         segment = m_segments.last() + positionInSegment;
346
347     unsigned segmentFreeSpace = segmentSize - positionInSegment;
348     unsigned bytesToCopy = std::min(length, segmentFreeSpace);
349
350     for (;;) {
351         memcpy(segment, data, bytesToCopy);
352         if (static_cast<unsigned>(length) == bytesToCopy)
353             break;
354
355         length -= bytesToCopy;
356         data += bytesToCopy;
357         segment = allocateSegment();
358         m_segments.append(segment);
359         bytesToCopy = std::min(length, segmentSize);
360     }
361 #else
362     m_size += length;
363     if (m_buffer->data.isEmpty())
364         m_buffer->data.reserveInitialCapacity(length);
365     appendToDataBuffer(data, length);
366 #endif
367 }
368
369 void SharedBuffer::append(const Vector<char>& data)
370 {
371     append(data.data(), data.size());
372 }
373
374 void SharedBuffer::clear()
375 {
376     clearPlatformData();
377     
378 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
379     for (unsigned i = 0; i < m_segments.size(); ++i)
380         freeSegment(m_segments[i]);
381
382     m_segments.clear();
383 #else
384     m_dataArray.clear();
385 #endif
386
387     m_size = 0;
388     clearDataBuffer();
389     m_purgeableBuffer.clear();
390 }
391
392 PassRefPtr<SharedBuffer> SharedBuffer::copy() const
393 {
394     RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer));
395     if (m_purgeableBuffer || hasPlatformData()) {
396         clone->append(data(), size());
397         return clone;
398     }
399
400     clone->m_size = m_size;
401     clone->m_buffer->data.reserveCapacity(m_size);
402     clone->m_buffer->data.append(m_buffer->data.data(), m_buffer->data.size());
403 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
404     for (unsigned i = 0; i < m_segments.size(); ++i)
405         clone->m_buffer->data.append(m_segments[i], segmentSize);
406 #else
407     for (unsigned i = 0; i < m_dataArray.size(); ++i)
408         clone->append(m_dataArray[i].get());
409 #endif
410     return clone;
411 }
412
413 PassOwnPtr<PurgeableBuffer> SharedBuffer::releasePurgeableBuffer()
414
415     ASSERT(hasOneRef()); 
416     return m_purgeableBuffer.release(); 
417 }
418
419 void SharedBuffer::duplicateDataBufferIfNecessary() const
420 {
421     if (m_buffer->hasOneRef() || m_size <= m_buffer->data.capacity())
422         return;
423
424     RefPtr<DataBuffer> newBuffer = adoptRef(new DataBuffer);
425     newBuffer->data.reserveInitialCapacity(m_size);
426     newBuffer->data = m_buffer->data;
427     m_buffer = newBuffer.release();
428 }
429
430 void SharedBuffer::appendToDataBuffer(const char *data, unsigned length) const
431 {
432     duplicateDataBufferIfNecessary();
433     m_buffer->data.append(data, length);
434 }
435
436 void SharedBuffer::clearDataBuffer()
437 {
438     if (!m_buffer->hasOneRef())
439         m_buffer = adoptRef(new DataBuffer);
440     else
441         m_buffer->data.clear();
442 }
443
444 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
445 void SharedBuffer::copyBufferAndClear(char* destination, unsigned bytesToCopy) const
446 {
447     for (unsigned i = 0; i < m_segments.size(); ++i) {
448         unsigned effectiveBytesToCopy = std::min(bytesToCopy, segmentSize);
449         memcpy(destination, m_segments[i], effectiveBytesToCopy);
450         destination += effectiveBytesToCopy;
451         bytesToCopy -= effectiveBytesToCopy;
452         freeSegment(m_segments[i]);
453     }
454     m_segments.clear();
455 }
456 #endif
457
458 const Vector<char>& SharedBuffer::buffer() const
459 {
460 #if ENABLE(DISK_IMAGE_CACHE)
461     ASSERT(!isMemoryMapped());
462 #endif
463     unsigned bufferSize = m_buffer->data.size();
464     if (m_size > bufferSize) {
465         duplicateDataBufferIfNecessary();
466         m_buffer->data.resize(m_size);
467         copyBufferAndClear(m_buffer->data.data() + bufferSize, m_size - bufferSize);
468     }
469     return m_buffer->data;
470 }
471
472 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const
473 {
474     unsigned totalSize = size();
475     if (position >= totalSize) {
476         someData = 0;
477         return 0;
478     }
479
480 #if ENABLE(DISK_IMAGE_CACHE)
481     ASSERT(position < size());
482     if (isMemoryMapped()) {
483         const char* data = static_cast<const char*>(diskImageCache().dataForItem(m_diskImageCacheId));
484         someData = data + position;
485         return size() - position;
486     }
487 #endif
488
489     if (hasPlatformData() || m_purgeableBuffer) {
490         ASSERT_WITH_SECURITY_IMPLICATION(position < size());
491         someData = data() + position;
492         return totalSize - position;
493     }
494
495     ASSERT_WITH_SECURITY_IMPLICATION(position < m_size);
496     unsigned consecutiveSize = m_buffer->data.size();
497     if (position < consecutiveSize) {
498         someData = m_buffer->data.data() + position;
499         return consecutiveSize - position;
500     }
501  
502     position -= consecutiveSize;
503 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK)
504     unsigned segments = m_segments.size();
505     unsigned maxSegmentedSize = segments * segmentSize;
506     unsigned segment = segmentIndex(position);
507     if (segment < segments) {
508         unsigned bytesLeft = totalSize - consecutiveSize;
509         unsigned segmentedSize = std::min(maxSegmentedSize, bytesLeft);
510
511         unsigned positionInSegment = offsetInSegment(position);
512         someData = m_segments[segment] + positionInSegment;
513         return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment;
514     }
515     ASSERT_NOT_REACHED();
516     return 0;
517 #else
518     return copySomeDataFromDataArray(someData, position);
519 #endif
520 }
521
522 #if !USE(CF) && !USE(SOUP)
523
524 inline void SharedBuffer::clearPlatformData()
525 {
526 }
527
528 inline void SharedBuffer::maybeTransferPlatformData()
529 {
530 }
531
532 inline bool SharedBuffer::hasPlatformData() const
533 {
534     return false;
535 }
536
537 inline const char* SharedBuffer::platformData() const
538 {
539     ASSERT_NOT_REACHED();
540
541     return 0;
542 }
543
544 inline unsigned SharedBuffer::platformDataSize() const
545 {
546     ASSERT_NOT_REACHED();
547     
548     return 0;
549 }
550
551 inline bool SharedBuffer::maybeAppendPlatformData(SharedBuffer*)
552 {
553     return false;
554 }
555
556 #endif
557
558 PassRefPtr<SharedBuffer> utf8Buffer(const String& string)
559 {
560     // Allocate a buffer big enough to hold all the characters.
561     const int length = string.length();
562     Vector<char> buffer(length * 3);
563
564     // Convert to runs of 8-bit characters.
565     char* p = buffer.data();
566     WTF::Unicode::ConversionResult result;
567     if (length) {
568         if (string.is8Bit()) {
569             const LChar* d = string.characters8();
570             result = WTF::Unicode::convertLatin1ToUTF8(&d, d + length, &p, p + buffer.size());
571         } else {
572             const UChar* d = string.characters16();
573             result = WTF::Unicode::convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), true);
574         }
575         if (result != WTF::Unicode::conversionOK)
576             return nullptr;
577     }
578
579     buffer.shrink(p - buffer.data());
580     return SharedBuffer::adoptVector(buffer);
581 }
582
583 } // namespace WebCore