Loading parser/htmlparser/nsExpatDriver.cpp +51 −4 Original line number Diff line number Diff line Loading @@ -46,6 +46,18 @@ using mozilla::LogLevel; using mozilla::MakeStringSpan; using mozilla::dom::Document; // We only pass chunks of length sMaxChunkLength to Expat in the RLBOX sandbox. // The RLBOX sandbox has a limited amount of memory, and we have to account for // other memory use by Expat (including the buffering it does). // Note that sMaxChunkLength is in number of characters. #ifdef DEBUG // On debug builds we set a much lower limit (1kB) to try to hit boundary // conditions more frequently. static const uint32_t sMaxChunkLength = 1024 / sizeof(char16_t); #else static const uint32_t sMaxChunkLength = (128 * 1024) / sizeof(char16_t); #endif #define kExpatSeparatorChar 0xFFFF static const char16_t kUTF16[] = {'U', 'T', 'F', '-', '1', '6', '\0'}; Loading Loading @@ -1108,6 +1120,39 @@ nsresult nsExpatDriver::HandleError() { return NS_ERROR_HTMLPARSER_STOPPARSING; } // Because we need to allocate a buffer in the RLBOX sandbox, and copy the data // to it for Expat to parse, we are limited in size by the memory available in // the RLBOX sandbox. nsExpatDriver::ChunkAndParseBuffer divides the buffer into // chunks of sMaxChunkLength characters or less, and passes them to // nsExpatDriver::ParseBuffer. That should ensure that we almost never run out // of memory in the sandbox. void nsExpatDriver::ChunkAndParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aPassedToExpat, uint32_t* aConsumed) { *aConsumed = 0; uint32_t remainder = aLength; while (remainder > sMaxChunkLength) { uint32_t consumed = 0; ParseBuffer(aBuffer, sMaxChunkLength, /* aIsFinal = */ false, &consumed); aBuffer += sMaxChunkLength; remainder -= sMaxChunkLength; *aConsumed += consumed; if (NS_FAILED(mInternalState)) { // Stop parsing if there's an error (including if we're blocked or // interrupted). *aPassedToExpat = aLength - remainder; return; } } uint32_t consumed = 0; ParseBuffer(aBuffer, remainder, aIsFinal, &consumed); *aConsumed += consumed; *aPassedToExpat = aLength; } void nsExpatDriver::ParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aConsumed) { NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?"); Loading Loading @@ -1156,8 +1201,6 @@ void nsExpatDriver::ParseBuffer(const char16_t* aBuffer, uint32_t aLength, // Consumed something. *aConsumed = (parserBytesConsumed - parserBytesBefore) / sizeof(char16_t); NS_ASSERTION(*aConsumed <= aLength + mExpatBuffered, "Too many bytes consumed?"); NS_ASSERTION(status != XML_STATUS_SUSPENDED || BlockedOrInterrupted(), "Inconsistent expat suspension state."); Loading Loading @@ -1236,8 +1279,12 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) { NS_ConvertUTF16toUTF8(start.get(), length).get())); } uint32_t passedToExpat; uint32_t consumed; ParseBuffer(buffer, length, noMoreBuffers, &consumed); ChunkAndParseBuffer(buffer, length, noMoreBuffers, &passedToExpat, &consumed); MOZ_ASSERT_IF(passedToExpat != length, NS_FAILED(mInternalState)); MOZ_ASSERT(consumed <= passedToExpat + mExpatBuffered); if (consumed > 0) { nsScannerIterator oldExpatPosition = currentExpatPosition; currentExpatPosition.advance(consumed); Loading Loading @@ -1269,7 +1316,7 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) { } } mExpatBuffered += length - consumed; mExpatBuffered += passedToExpat - consumed; if (BlockedOrInterrupted()) { MOZ_LOG(gExpatDriverLog, LogLevel::Debug, Loading parser/htmlparser/nsExpatDriver.h +10 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,16 @@ class nsExpatDriver : public nsIDTD, public nsITokenizer { */ void ParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aConsumed); /** * Wrapper for ParseBuffer. If the buffer is too large to be copied into the * sandbox all at once, splits it into chunks and invokes ParseBuffer in a * loop. */ void ChunkAndParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aPassedToExpat, uint32_t* aConsumed); nsresult HandleError(); void MaybeStopParser(nsresult aState); Loading Loading
parser/htmlparser/nsExpatDriver.cpp +51 −4 Original line number Diff line number Diff line Loading @@ -46,6 +46,18 @@ using mozilla::LogLevel; using mozilla::MakeStringSpan; using mozilla::dom::Document; // We only pass chunks of length sMaxChunkLength to Expat in the RLBOX sandbox. // The RLBOX sandbox has a limited amount of memory, and we have to account for // other memory use by Expat (including the buffering it does). // Note that sMaxChunkLength is in number of characters. #ifdef DEBUG // On debug builds we set a much lower limit (1kB) to try to hit boundary // conditions more frequently. static const uint32_t sMaxChunkLength = 1024 / sizeof(char16_t); #else static const uint32_t sMaxChunkLength = (128 * 1024) / sizeof(char16_t); #endif #define kExpatSeparatorChar 0xFFFF static const char16_t kUTF16[] = {'U', 'T', 'F', '-', '1', '6', '\0'}; Loading Loading @@ -1108,6 +1120,39 @@ nsresult nsExpatDriver::HandleError() { return NS_ERROR_HTMLPARSER_STOPPARSING; } // Because we need to allocate a buffer in the RLBOX sandbox, and copy the data // to it for Expat to parse, we are limited in size by the memory available in // the RLBOX sandbox. nsExpatDriver::ChunkAndParseBuffer divides the buffer into // chunks of sMaxChunkLength characters or less, and passes them to // nsExpatDriver::ParseBuffer. That should ensure that we almost never run out // of memory in the sandbox. void nsExpatDriver::ChunkAndParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aPassedToExpat, uint32_t* aConsumed) { *aConsumed = 0; uint32_t remainder = aLength; while (remainder > sMaxChunkLength) { uint32_t consumed = 0; ParseBuffer(aBuffer, sMaxChunkLength, /* aIsFinal = */ false, &consumed); aBuffer += sMaxChunkLength; remainder -= sMaxChunkLength; *aConsumed += consumed; if (NS_FAILED(mInternalState)) { // Stop parsing if there's an error (including if we're blocked or // interrupted). *aPassedToExpat = aLength - remainder; return; } } uint32_t consumed = 0; ParseBuffer(aBuffer, remainder, aIsFinal, &consumed); *aConsumed += consumed; *aPassedToExpat = aLength; } void nsExpatDriver::ParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aConsumed) { NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?"); Loading Loading @@ -1156,8 +1201,6 @@ void nsExpatDriver::ParseBuffer(const char16_t* aBuffer, uint32_t aLength, // Consumed something. *aConsumed = (parserBytesConsumed - parserBytesBefore) / sizeof(char16_t); NS_ASSERTION(*aConsumed <= aLength + mExpatBuffered, "Too many bytes consumed?"); NS_ASSERTION(status != XML_STATUS_SUSPENDED || BlockedOrInterrupted(), "Inconsistent expat suspension state."); Loading Loading @@ -1236,8 +1279,12 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) { NS_ConvertUTF16toUTF8(start.get(), length).get())); } uint32_t passedToExpat; uint32_t consumed; ParseBuffer(buffer, length, noMoreBuffers, &consumed); ChunkAndParseBuffer(buffer, length, noMoreBuffers, &passedToExpat, &consumed); MOZ_ASSERT_IF(passedToExpat != length, NS_FAILED(mInternalState)); MOZ_ASSERT(consumed <= passedToExpat + mExpatBuffered); if (consumed > 0) { nsScannerIterator oldExpatPosition = currentExpatPosition; currentExpatPosition.advance(consumed); Loading Loading @@ -1269,7 +1316,7 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) { } } mExpatBuffered += length - consumed; mExpatBuffered += passedToExpat - consumed; if (BlockedOrInterrupted()) { MOZ_LOG(gExpatDriverLog, LogLevel::Debug, Loading
parser/htmlparser/nsExpatDriver.h +10 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,16 @@ class nsExpatDriver : public nsIDTD, public nsITokenizer { */ void ParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aConsumed); /** * Wrapper for ParseBuffer. If the buffer is too large to be copied into the * sandbox all at once, splits it into chunks and invokes ParseBuffer in a * loop. */ void ChunkAndParseBuffer(const char16_t* aBuffer, uint32_t aLength, bool aIsFinal, uint32_t* aPassedToExpat, uint32_t* aConsumed); nsresult HandleError(); void MaybeStopParser(nsresult aState); Loading