From eb05619abf7b2f100dd3bfc0a32d5d2f59875568 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sun, 24 May 2015 05:38:00 +0900 Subject: [PATCH] Bug 1159973 - Abort parsing when TokenStream::SourceCoords hits OOM. r=jorendorff --- js/src/asmjs/AsmJSModule.cpp | 3 ++- js/src/frontend/BytecodeEmitter.cpp | 5 +++- js/src/frontend/Parser.cpp | 3 ++- js/src/frontend/TokenStream.cpp | 36 +++++++++++++++++++---------- js/src/frontend/TokenStream.h | 30 +++++++++++++++--------- 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index b3f0cfc1999..d022c031005 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -2289,7 +2289,8 @@ js::LookupAsmJSModuleInCache(ExclusiveContext* cx, if (!atEnd) return true; - parser.tokenStream.advance(module->srcEndBeforeCurly()); + if (!parser.tokenStream.advance(module->srcEndBeforeCurly())) + return false; { // Delay flushing until dynamic linking. diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 33421451011..35bd6e2f34e 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -417,7 +417,10 @@ bool BytecodeEmitter::updateLineNumberNotes(uint32_t offset) { TokenStream* ts = &parser->tokenStream; - if (!ts->srcCoords.isOnThisLine(offset, currentLine())) { + bool onThisLine; + if (!ts->srcCoords.isOnThisLine(offset, currentLine(), &onThisLine)) + return ts->reportError(JSMSG_OUT_OF_MEMORY); + if (!onThisLine) { unsigned line = ts->srcCoords.lineNum(offset); unsigned delta = line - currentLine(); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 1451948bf15..8d520af769a 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1971,7 +1971,8 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, // while LazyScript::{begin,end} offsets are relative to the outermost // script source. uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column(); - tokenStream.advance(fun->lazyScript()->end() - userbufBase); + if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase)) + return false; *pbodyProcessed = true; return true; diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 7dbc84e1104..f872036dd73 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -162,7 +162,7 @@ TokenStream::SourceCoords::SourceCoords(ExclusiveContext* cx, uint32_t ln) lineStartOffsets_.infallibleAppend(maxPtr); } -MOZ_ALWAYS_INLINE void +MOZ_ALWAYS_INLINE bool TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset) { uint32_t lineIndex = lineNumToIndex(lineNum); @@ -171,21 +171,21 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset) MOZ_ASSERT(lineStartOffsets_[0] == 0 && lineStartOffsets_[sentinelIndex] == MAX_PTR); if (lineIndex == sentinelIndex) { - // We haven't seen this newline before. Update lineStartOffsets_. - // We ignore any failures due to OOM -- because we always have a - // sentinel node, it'll just be like the newline wasn't present. I.e. - // the line numbers will be wrong, but the code won't crash or anything - // like that. - lineStartOffsets_[lineIndex] = lineStartOffset; - + // We haven't seen this newline before. Update lineStartOffsets_ + // only if lineStartOffsets_.append succeeds, to keep sentinel. + // Otherwise return false to tell TokenStream about OOM. uint32_t maxPtr = MAX_PTR; - (void)lineStartOffsets_.append(maxPtr); + if (!lineStartOffsets_.append(maxPtr)) + return false; + lineStartOffsets_[lineIndex] = lineStartOffset; } else { // We have seen this newline before (and ungot it). Do nothing (other // than checking it hasn't mysteriously changed). - MOZ_ASSERT(lineStartOffsets_[lineIndex] == lineStartOffset); + // This path can be executed after hitting OOM, so check lineIndex. + MOZ_ASSERT_IF(lineIndex < sentinelIndex, lineStartOffsets_[lineIndex] == lineStartOffset); } + return true; } MOZ_ALWAYS_INLINE bool @@ -360,7 +360,8 @@ TokenStream::updateLineInfoForEOL() prevLinebase = linebase; linebase = userbuf.offset(); lineno++; - srcCoords.add(lineno, linebase); + if (!srcCoords.add(lineno, linebase)) + flags.hitOOM = true; } MOZ_ALWAYS_INLINE void @@ -493,7 +494,7 @@ TokenStream::TokenBuf::findEOLMax(size_t start, size_t max) return start + n; } -void +bool TokenStream::advance(size_t position) { const char16_t* end = userbuf.rawCharPtrAt(position); @@ -504,6 +505,11 @@ TokenStream::advance(size_t position) cur->pos.begin = userbuf.offset(); MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type)); lookahead = 0; + + if (flags.hitOOM) + return reportError(JSMSG_OUT_OF_MEMORY); + + return true; } void @@ -1631,6 +1637,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) MOZ_CRASH("should have jumped to |out| or |error|"); out: + if (flags.hitOOM) + return reportError(JSMSG_OUT_OF_MEMORY); + flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); MOZ_ASSERT(IsTokenSane(tp)); @@ -1638,6 +1647,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) return true; error: + if (flags.hitOOM) + return reportError(JSMSG_OUT_OF_MEMORY); + flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); MOZ_MAKE_MEM_UNDEFINED(&tp->type, sizeof(tp->type)); diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 0ff3400e7f5..fa6eb420247 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -351,9 +351,10 @@ class MOZ_STACK_CLASS TokenStream bool sawOctalEscape:1; // Saw an octal character escape. bool hadError:1; // Hit a syntax error, at start or during a // token. + bool hitOOM:1; // Hit OOM. Flags() - : isEOF(), isDirtyLine(), sawOctalEscape(), hadError() + : isEOF(), isDirtyLine(), sawOctalEscape(), hadError(), hitOOM() {} }; @@ -435,10 +436,15 @@ class MOZ_STACK_CLASS TokenStream // it's the same as the line that the current token ends on, that's a // stronger condition than what we are looking for, and we don't need // to return TOK_EOL. - if (lookahead != 0 && srcCoords.isOnThisLine(curr.pos.end, lineno)) { - MOZ_ASSERT(!flags.hadError); - *ttp = tokens[(cursor + 1) & ntokensMask].type; - return true; + if (lookahead != 0) { + bool onThisLine; + if (!srcCoords.isOnThisLine(curr.pos.end, lineno, &onThisLine)) + return reportError(JSMSG_OUT_OF_MEMORY); + if (onThisLine) { + MOZ_ASSERT(!flags.hadError); + *ttp = tokens[(cursor + 1) & ntokensMask].type; + return true; + } } // The above check misses two cases where we don't have to return @@ -525,7 +531,7 @@ class MOZ_STACK_CLASS TokenStream Token lookaheadTokens[maxLookahead]; }; - void advance(size_t position); + bool advance(size_t position); void tell(Position*); void seek(const Position& pos); bool seek(const Position& pos, const TokenStream& other); @@ -626,14 +632,16 @@ class MOZ_STACK_CLASS TokenStream public: SourceCoords(ExclusiveContext* cx, uint32_t ln); - void add(uint32_t lineNum, uint32_t lineStartOffset); + bool add(uint32_t lineNum, uint32_t lineStartOffset); bool fill(const SourceCoords& other); - bool isOnThisLine(uint32_t offset, uint32_t lineNum) const { + bool isOnThisLine(uint32_t offset, uint32_t lineNum, bool* onThisLine) const { uint32_t lineIndex = lineNumToIndex(lineNum); - MOZ_ASSERT(lineIndex + 1 < lineStartOffsets_.length()); // +1 due to sentinel - return lineStartOffsets_[lineIndex] <= offset && - offset < lineStartOffsets_[lineIndex + 1]; + if (lineIndex + 1 >= lineStartOffsets_.length()) // +1 due to sentinel + return false; + *onThisLine = lineStartOffsets_[lineIndex] <= offset && + offset < lineStartOffsets_[lineIndex + 1]; + return true; } uint32_t lineNum(uint32_t offset) const;