Bug 953013 - throw exceptions for uncorrectly-interpreted regular expressions instead of treating them as non-matching. r=jandem
authorTill Schneidereit <till@tillschneidereit.net>
Fri, 10 Jan 2014 15:24:32 +0100
changeset 162951 9ecbd1ca14e2a28dd0fd65c005753bb15c820834
parent 162950 d20c762cc1b6b00b0560baa06534fba8c7eee992
child 162952 2a9e0c5b7eeac1c41440ff57be1428046bb795c0
push id25975
push userryanvm@gmail.com
push dateFri, 10 Jan 2014 19:46:47 +0000
treeherderautoland@e89afc241513 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs953013
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 953013 - throw exceptions for uncorrectly-interpreted regular expressions instead of treating them as non-matching. r=jandem
js/src/jit-test/tests/basic/regexp-match-limit.js
js/src/js.msg
js/src/vm/RegExpObject.cpp
js/src/yarr/Yarr.h
js/src/yarr/YarrInterpreter.cpp
js/src/yarr/YarrPattern.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regexp-match-limit.js
@@ -0,0 +1,9 @@
+// See bug 953013
+
+load(libdir + "asserts.js");
+
+function test() {
+    var input = Array(999999+1).join('a');
+    var result = /^([\w])+$/.test(input);
+}
+assertThrowsInstanceOf(test, InternalError);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -350,17 +350,17 @@ MSG_DEF(JSMSG_NO_REST_NAME,           29
 MSG_DEF(JSMSG_ARGUMENTS_AND_REST,     297, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter")
 MSG_DEF(JSMSG_FUNCTION_ARGUMENTS_AND_REST, 298, 0, JSEXN_ERR, "the 'arguments' property of a function with a rest parameter may not be used")
 MSG_DEF(JSMSG_REST_WITH_DEFAULT,      299, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
 MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
 MSG_DEF(JSMSG_YIELD_IN_DEFAULT,       301, 0, JSEXN_SYNTAXERR, "yield in default expression")
 MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED,  302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}")
 MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR,      "{0} is being assigned a {1}, but already has one")
 MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG,      304, 0, JSEXN_RANGEERR, "invalid parallel method argument")
-MSG_DEF(JSMSG_UNUSED305,              305, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_REGEXP_RUNTIME_ERROR,   305, 0, JSEXN_INTERNALERR, "an error occurred while executing regular expression")
 MSG_DEF(JSMSG_UNUSED306,              306, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED307,              307, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")
 MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE,   310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
 MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE,    311, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
 MSG_DEF(JSMSG_CANT_REPORT_NEW,        312, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
 MSG_DEF(JSMSG_CANT_REPORT_INVALID,    313, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -409,16 +409,17 @@ RegExpShared::reportYarrError(ExclusiveC
       COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
       COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
       COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
       COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
       COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
       COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
       COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
       COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
+      COMPILE_EMSG(RuntimeError, JSMSG_REGEXP_RUNTIME_ERROR);
 #undef COMPILE_EMSG
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown Yarr error code");
     }
 }
 
 bool
 RegExpShared::checkSyntax(ExclusiveContext *cx, TokenStream *tokenStream, JSLinearString *source)
@@ -553,16 +554,21 @@ RegExpShared::execute(JSContext *cx, con
     if (codeBlock.isFallBack())
         result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
     else
         result = codeBlock.execute(chars, start, length, (int *)outputBuf).start;
 #else
     result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
 #endif
 
+    if (result == JSC::Yarr::offsetError) {
+        reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError);
+        return RegExpRunStatus_Error;
+    }
+
     if (result == JSC::Yarr::offsetNoMatch)
         return RegExpRunStatus_Success_NotFound;
 
     matches.displace(displacement);
     matches.checkAgainst(origLength);
     *lastIndex = matches[0].limit;
     return RegExpRunStatus_Success;
 }
@@ -612,16 +618,21 @@ RegExpShared::executeMatchOnly(JSContext
     JS_ASSERT(hasBytecode());
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     if (!matches.initArray(pairCount()))
         return RegExpRunStatus_Error;
 
     unsigned result =
         JSC::Yarr::interpret(cx, bytecode, chars, length, start, matches.rawBuf());
 
+    if (result == JSC::Yarr::offsetError) {
+        reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError);
+        return RegExpRunStatus_Error;
+    }
+
     if (result == JSC::Yarr::offsetNoMatch)
         return RegExpRunStatus_Success_NotFound;
 
     match = MatchPair(result, matches[0].limit);
     match.displace(displacement);
 
 #ifdef DEBUG
     matches.displace(displacement);
--- a/js/src/yarr/Yarr.h
+++ b/js/src/yarr/Yarr.h
@@ -42,16 +42,17 @@ namespace JSC { namespace Yarr {
 #define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative.
 #define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1
 #define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers.
 #define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1
 #define YarrStackSpaceForBackTrackInfoParentheses 2
 
 static const unsigned quantifyInfinite = UINT_MAX;
 static const unsigned offsetNoMatch = (unsigned)-1;
+static const unsigned offsetError = (unsigned)-2;
 
 // The below limit restricts the number of "recursive" match calls in order to
 // avoid spending exponential time on complex regular expressions.
 static const unsigned matchLimit = 1000000;
 
 enum JSRegExpResult {
     JSRegExpMatch = 1,
     JSRegExpNoMatch = 0,
--- a/js/src/yarr/YarrInterpreter.cpp
+++ b/js/src/yarr/YarrInterpreter.cpp
@@ -1444,17 +1444,20 @@ public:
             output[0] = context->matchBegin;
             output[1] = context->matchEnd;
         }
 
         freeDisjunctionContext(context);
 
         pattern->m_allocator->stopAllocator();
 
-        ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch));
+        if (result != JSRegExpMatch && result != JSRegExpNoMatch)
+            output[0] = offsetError;
+        else
+            ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch));
         return output[0];
     }
 
     Interpreter(JSContext *cx, BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start)
         : cx(cx)
         , pattern(pattern)
         , output(output)
         , input(input, start, length)
--- a/js/src/yarr/YarrPattern.h
+++ b/js/src/yarr/YarrPattern.h
@@ -44,16 +44,17 @@ enum ErrorCode {
     QuantifierTooLarge,
     MissingParentheses,
     ParenthesesUnmatched,
     ParenthesesTypeInvalid,
     CharacterClassUnmatched,
     CharacterClassOutOfOrder,
     CharacterClassInvalidRange,
     EscapeUnterminated,
+    RuntimeError,
     NumberOfErrorCodes
 };
 
 static inline const char* errorMessage(ErrorCode code)
 {
 
 #define REGEXP_ERROR_PREFIX "Invalid regular expression: "
    // The order of this array must match the ErrorCode enum.