Commit 50d5b0a9 authored by Peter Van der Beken's avatar Peter Van der Beken
Browse files

Bug 1745239 - Chunk XML parsing to 64k characters at a time. r=bholley, a=dsmith

parent 473db8d3
Loading
Loading
Loading
Loading
+51 −4
Original line number Diff line number Diff line
@@ -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'};
@@ -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), "?");
@@ -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.");
@@ -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);
@@ -1269,7 +1316,7 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) {
      }
    }

    mExpatBuffered += length - consumed;
    mExpatBuffered += passedToExpat - consumed;

    if (BlockedOrInterrupted()) {
      MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
+10 −0
Original line number Diff line number Diff line
@@ -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);