Changeset 171526 in webkit
- Timestamp:
- Jul 24, 2014, 2:37:43 PM (11 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r171513 r171526 1 2014-07-24 Pratik Solanki <psolanki@apple.com> 2 3 Sharing SharedBuffer between WebCore and ImageIO is racy and crash prone 4 https://bugs.webkit.org/show_bug.cgi?id=135069 5 <rdar://problem/17470655> 6 7 Reviewed by Simon Fraser. 8 9 When passing image data to ImageIO for decoding, we pass an NSData subclass that is a wraper 10 around SharedBuffer. This can be a problem when ImageIO tries to access the data on the CA 11 thread. End result is data corruption on large image loads and potential crashes. The fix is 12 to have SharedBuffer create a copy of its data if the data has been passed to ImageIO and 13 might be accessed concurrently. 14 15 Since Vector is not refcounted, we do this by having a new refcounted object in SharedBuffer 16 that contains the buffer and we pass that in our NSData subclass WebCoreSharedBufferData. 17 Code that would result in the Vector memory moving e.g. append(), resize(), now checks to 18 see if the buffer was shared and if so, will create a new copy of the vector. This ensures 19 that the main thread does not end up invalidating the vector memory that we have passed it 20 to ImageIO. 21 22 No new tests because no functional changes. 23 24 * loader/cache/CachedResource.cpp: 25 (WebCore::CachedResource::makePurgeable): 26 Remove early return - createPurgeableMemory() has the correct check now. 27 * platform/SharedBuffer.cpp: 28 (WebCore::SharedBuffer::SharedBuffer): 29 (WebCore::SharedBuffer::adoptVector): 30 (WebCore::SharedBuffer::createPurgeableBuffer): 31 Don't create purgeable buffer if we are sharing the buffer. 32 (WebCore::SharedBuffer::append): 33 (WebCore::SharedBuffer::clear): 34 (WebCore::SharedBuffer::copy): 35 (WebCore::SharedBuffer::duplicateDataBufferIfNecessary): Added. 36 Create a new copy of the data if we have shared the buffer and if appending to it would 37 exceed the capacity of the vector resulting in memmove. 38 (WebCore::SharedBuffer::appendToInternalBuffer): Added. 39 (WebCore::SharedBuffer::clearInternalBuffer): Added. 40 (WebCore::SharedBuffer::buffer): 41 Create a new copy of the buffer if we have shared it. 42 (WebCore::SharedBuffer::getSomeData): 43 * platform/SharedBuffer.h: 44 * platform/cf/SharedBufferCF.cpp: 45 (WebCore::SharedBuffer::SharedBuffer): 46 (WebCore::SharedBuffer::singleDataArrayBuffer): 47 (WebCore::SharedBuffer::maybeAppendDataArray): 48 * platform/mac/SharedBufferMac.mm: 49 Pass the InternalBuffer object to WebCoreSharedBufferData 50 (-[WebCoreSharedBufferData dealloc]): 51 (-[WebCoreSharedBufferData initWithSharedBufferInternalBuffer:]): 52 (-[WebCoreSharedBufferData length]): 53 (-[WebCoreSharedBufferData bytes]): 54 (WebCore::SharedBuffer::createNSData): 55 Call createCFData() instead of duplicating code. 56 (WebCore::SharedBuffer::createCFData): 57 If the data is in purgeable memory, make a copy of it since m_buffer was cleared when 58 creating the purgeable buffer. 59 (-[WebCoreSharedBufferData initWithSharedBuffer:]): Deleted. 60 1 61 2014-07-24 peavo@outlook.com <peavo@outlook.com> 2 62 -
trunk/Source/WebCore/loader/cache/CachedResource.cpp
r171036 r171526 812 812 return false; 813 813 814 // Should not make buffer purgeable if it has refs other than this since we don't want two copies.815 if (!m_data->hasOneRef())816 return false;817 818 814 m_data->createPurgeableBuffer(); 819 815 if (!m_data->hasPurgeableBuffer()) -
trunk/Source/WebCore/platform/SharedBuffer.cpp
r171289 r171526 66 66 SharedBuffer::SharedBuffer() 67 67 : m_size(0) 68 , m_buffer(adoptRef(new DataBuffer)) 68 69 , m_shouldUsePurgeableMemory(false) 69 70 #if ENABLE(DISK_IMAGE_CACHE) … … 78 79 SharedBuffer::SharedBuffer(unsigned size) 79 80 : m_size(size) 80 , m_buffer( size)81 , m_buffer(adoptRef(new DataBuffer)) 81 82 , m_shouldUsePurgeableMemory(false) 82 83 #if ENABLE(DISK_IMAGE_CACHE) … … 91 92 SharedBuffer::SharedBuffer(const char* data, unsigned size) 92 93 : m_size(0) 94 , m_buffer(adoptRef(new DataBuffer)) 93 95 , m_shouldUsePurgeableMemory(false) 94 96 #if ENABLE(DISK_IMAGE_CACHE) … … 104 106 SharedBuffer::SharedBuffer(const unsigned char* data, unsigned size) 105 107 : m_size(0) 108 , m_buffer(adoptRef(new DataBuffer)) 106 109 , m_shouldUsePurgeableMemory(false) 107 110 #if ENABLE(DISK_IMAGE_CACHE) … … 130 133 { 131 134 RefPtr<SharedBuffer> buffer = create(); 132 buffer->m_buffer .swap(vector);133 buffer->m_size = buffer->m_buffer .size();135 buffer->m_buffer->data.swap(vector); 136 buffer->m_size = buffer->m_buffer->data.size(); 134 137 return buffer.release(); 135 138 } … … 233 236 #endif 234 237 235 if (! hasOneRef())238 if (!m_buffer->hasOneRef()) 236 239 return; 237 240 … … 243 246 if (!m_purgeableBuffer) 244 247 return; 245 unsigned bufferSize = m_buffer .size();248 unsigned bufferSize = m_buffer->data.size(); 246 249 if (bufferSize) { 247 memcpy(destination, m_buffer .data(), bufferSize);250 memcpy(destination, m_buffer->data.data(), bufferSize); 248 251 destination += bufferSize; 249 m_buffer.clear();252 (const_cast<SharedBuffer*>(this))->clearDataBuffer(); 250 253 } 251 254 copyBufferAndClear(destination, m_size - bufferSize); … … 324 327 325 328 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK) 326 unsigned positionInSegment = offsetInSegment(m_size - m_buffer .size());329 unsigned positionInSegment = offsetInSegment(m_size - m_buffer->data.size()); 327 330 m_size += length; 328 331 329 332 if (m_size <= segmentSize) { 330 333 // No need to use segments for small resource data 331 if (m_buffer .isEmpty())332 m_buffer .reserveInitialCapacity(length);333 m_buffer.append(data, length);334 if (m_buffer->data.isEmpty()) 335 m_buffer->data.reserveInitialCapacity(length); 336 appendToDataBuffer(data, length); 334 337 return; 335 338 } … … 357 360 } 358 361 #else 359 if (m_buffer.isEmpty())360 m_buffer.reserveInitialCapacity(length);361 m_buffer.append(data, length);362 362 m_size += length; 363 if (m_buffer->data.isEmpty()) 364 m_buffer->data.reserveInitialCapacity(length); 365 appendToDataBuffer(data, length); 363 366 #endif 364 367 } … … 383 386 384 387 m_size = 0; 385 m_buffer.clear();388 clearDataBuffer(); 386 389 m_purgeableBuffer.clear(); 387 390 } … … 396 399 397 400 clone->m_size = m_size; 398 clone->m_buffer .reserveCapacity(m_size);399 clone->m_buffer .append(m_buffer.data(), m_buffer.size());401 clone->m_buffer->data.reserveCapacity(m_size); 402 clone->m_buffer->data.append(m_buffer->data.data(), m_buffer->data.size()); 400 403 #if !USE(NETWORK_CFDATA_ARRAY_CALLBACK) 401 404 for (unsigned i = 0; i < m_segments.size(); ++i) 402 clone->m_buffer .append(m_segments[i], segmentSize);405 clone->m_buffer->data.append(m_segments[i], segmentSize); 403 406 #else 404 407 for (unsigned i = 0; i < m_dataArray.size(); ++i) … … 412 415 ASSERT(hasOneRef()); 413 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(); 414 442 } 415 443 … … 433 461 ASSERT(!isMemoryMapped()); 434 462 #endif 435 unsigned bufferSize = m_buffer .size();463 unsigned bufferSize = m_buffer->data.size(); 436 464 if (m_size > bufferSize) { 437 m_buffer.resize(m_size); 438 copyBufferAndClear(m_buffer.data() + bufferSize, m_size - bufferSize); 439 } 440 return m_buffer; 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; 441 470 } 442 471 … … 465 494 466 495 ASSERT_WITH_SECURITY_IMPLICATION(position < m_size); 467 unsigned consecutiveSize = m_buffer .size();496 unsigned consecutiveSize = m_buffer->data.size(); 468 497 if (position < consecutiveSize) { 469 someData = m_buffer .data() + position;498 someData = m_buffer->data.data() + position; 470 499 return consecutiveSize - position; 471 500 } -
trunk/Source/WebCore/platform/SharedBuffer.h
r171497 r171526 32 32 #include <wtf/OwnPtr.h> 33 33 #include <wtf/RefCounted.h> 34 #include <wtf/ThreadSafeRefCounted.h> 34 35 #include <wtf/Vector.h> 35 36 #include <wtf/text/WTFString.h> … … 159 160 bool hasPlatformData() const; 160 161 162 struct DataBuffer : public ThreadSafeRefCounted<DataBuffer> { 163 Vector<char> data; 164 }; 165 161 166 private: 162 167 SharedBuffer(); … … 178 183 void copyBufferAndClear(char* destination, unsigned bytesToCopy) const; 179 184 185 void appendToDataBuffer(const char *, unsigned) const; 186 void duplicateDataBufferIfNecessary() const; 187 void clearDataBuffer(); 188 180 189 unsigned m_size; 181 mutable Vector<char> m_buffer; 190 mutable RefPtr<DataBuffer> m_buffer; 191 182 192 bool m_shouldUsePurgeableMemory; 183 193 mutable OwnPtr<PurgeableBuffer> m_purgeableBuffer; -
trunk/Source/WebCore/platform/cf/SharedBufferCF.cpp
r170975 r171526 40 40 SharedBuffer::SharedBuffer(CFDataRef cfData) 41 41 : m_size(0) 42 , m_buffer(adoptRef(new DataBuffer)) 42 43 , m_shouldUsePurgeableMemory(false) 43 44 #if ENABLE(DISK_IMAGE_CACHE) … … 129 130 SharedBuffer::SharedBuffer(CFArrayRef cfDataArray) 130 131 : m_size(0) 132 , m_buffer(adoptRef(new DataBuffer)) 131 133 , m_shouldUsePurgeableMemory(false) 132 134 #if ENABLE(DISK_IMAGE_CACHE) … … 188 190 // If we had previously copied data into m_buffer in copyDataArrayAndClear() or some other 189 191 // function, then we can't return a pointer to the CFDataRef buffer. 190 if (m_buffer .size())192 if (m_buffer->data.size()) 191 193 return 0; 192 194 … … 199 201 bool SharedBuffer::maybeAppendDataArray(SharedBuffer* data) 200 202 { 201 if (m_buffer .size() || m_cfData || !data->m_dataArray.size())203 if (m_buffer->data.size() || m_cfData || !data->m_dataArray.size()) 202 204 return false; 203 205 #if !ASSERT_DISABLED -
trunk/Source/WebCore/platform/mac/SharedBufferMac.mm
r171497 r171526 33 33 #include <wtf/PassRefPtr.h> 34 34 35 36 35 using namespace WebCore; 37 36 38 37 @interface WebCoreSharedBufferData : NSData 39 38 { 40 RefPtr<SharedBuffer > sharedBuffer;39 RefPtr<SharedBuffer::DataBuffer> buffer; 41 40 } 42 41 43 - (id)initWithSharedBuffer :(SharedBuffer*)buffer;42 - (id)initWithSharedBufferDataBuffer:(SharedBuffer::DataBuffer*)dataBuffer; 44 43 @end 45 44 … … 59 58 if (WebCoreObjCScheduleDeallocateOnMainThread([WebCoreSharedBufferData class], self)) 60 59 return; 61 60 62 61 [super dealloc]; 63 62 } … … 68 67 } 69 68 70 - (id)initWithSharedBuffer :(SharedBuffer*)buffer69 - (id)initWithSharedBufferDataBuffer:(SharedBuffer::DataBuffer*)dataBuffer 71 70 { 72 71 self = [super init]; 73 72 74 73 if (self) 75 sharedBuffer = buffer;76 74 buffer = dataBuffer; 75 77 76 return self; 78 77 } … … 80 79 - (NSUInteger)length 81 80 { 82 return sharedBuffer->size();81 return buffer->data.size(); 83 82 } 84 83 85 84 - (const void *)bytes 86 85 { 87 return reinterpret_cast<const void*>( sharedBuffer->data());86 return reinterpret_cast<const void*>(buffer->data.data()); 88 87 } 89 88 … … 99 98 RetainPtr<NSData> SharedBuffer::createNSData() 100 99 { 101 return adoptNS( [[WebCoreSharedBufferData alloc] initWithSharedBuffer:this]);100 return adoptNS((NSData *)createCFData().leakRef()); 102 101 } 103 102 … … 107 106 return m_cfData; 108 107 109 return adoptCF((CFDataRef)adoptNS([[WebCoreSharedBufferData alloc] initWithSharedBuffer:this]).leakRef()); 108 data(); // Force data into m_buffer from segments or data array. 109 if (hasPurgeableBuffer()) { 110 RefPtr<SharedBuffer::DataBuffer> copiedBuffer = adoptRef(new DataBuffer); 111 copiedBuffer->data.append(data(), size()); 112 return adoptCF(reinterpret_cast<CFDataRef>([[WebCoreSharedBufferData alloc] initWithSharedBufferDataBuffer:copiedBuffer.get()])); 113 } 114 115 return adoptCF(reinterpret_cast<CFDataRef>([[WebCoreSharedBufferData alloc] initWithSharedBufferDataBuffer:m_buffer.get()])); 110 116 } 111 117
Note:
See TracChangeset
for help on using the changeset viewer.