Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (203451 => 203452)
--- trunk/Source/_javascript_Core/ChangeLog 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-07-20 14:50:32 UTC (rev 203452)
@@ -1,3 +1,42 @@
+2016-07-20 Michael Saboff <msab...@apple.com>
+
+ CrashOnOverflow in JSC::Yarr::YarrPatternConstructor::setupAlternativeOffsets
+ https://bugs.webkit.org/show_bug.cgi?id=159954
+
+ Reviewed by Benjamin Poulain.
+
+ YarrPatternConstructor::setupAlternativeOffsets() is using the checked arithmetic class
+ Checked<>, for offset calculations. However the default use will just crash on
+ overflow. Instead we should stop processing and propagate the error up the call stack.
+
+ Consolidated explicit error string with the common RegExp parsing error logic.
+ Moved that logic to YarrPattern as that seems like a better common place to put it.
+
+ * jit/JITOperations.cpp:
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * tests/stress/regress-159954.js: New test.
+ * yarr/YarrParser.h:
+ (JSC::Yarr::Parser::CharacterClassParserDelegate::CharacterClassParserDelegate):
+ (JSC::Yarr::Parser::CharacterClassParserDelegate::atomPatternCharacter):
+ (JSC::Yarr::Parser::Parser):
+ (JSC::Yarr::Parser::isIdentityEscapeAnError):
+ (JSC::Yarr::Parser::parseEscape):
+ (JSC::Yarr::Parser::parseCharacterClass):
+ (JSC::Yarr::Parser::parseParenthesesBegin):
+ (JSC::Yarr::Parser::parseParenthesesEnd):
+ (JSC::Yarr::Parser::parseQuantifier):
+ (JSC::Yarr::Parser::parseTokens):
+ (JSC::Yarr::Parser::parse):
+ * yarr/YarrPattern.cpp:
+ (JSC::Yarr::YarrPatternConstructor::disjunction):
+ (JSC::Yarr::YarrPatternConstructor::setupDisjunctionOffsets):
+ (JSC::Yarr::YarrPatternConstructor::setupOffsets):
+ (JSC::Yarr::YarrPattern::errorMessage):
+ (JSC::Yarr::YarrPattern::compile):
+ * yarr/YarrPattern.h:
+ (JSC::Yarr::YarrPattern::reset):
+
2016-07-19 Filip Pizlo <fpi...@apple.com>
The default testing mode should not involve disabling the FTL JIT
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (203451 => 203452)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-07-20 14:50:32 UTC (rev 203452)
@@ -1137,7 +1137,7 @@
NativeCallFrameTracer tracer(&vm, exec);
RegExp* regexp = static_cast<RegExp*>(regexpPtr);
if (!regexp->isValid()) {
- vm.throwException(exec, createSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor.")));
+ vm.throwException(exec, createSyntaxError(exec, regexp->errorMessage()));
return JSValue::encode(jsUndefined());
}
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (203451 => 203452)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2016-07-20 14:50:32 UTC (rev 203452)
@@ -536,7 +536,7 @@
LLINT_BEGIN();
RegExp* regExp = exec->codeBlock()->regexp(pc[2].u.operand);
if (!regExp->isValid())
- LLINT_THROW(createSyntaxError(exec, "Invalid flag supplied to RegExp constructor."));
+ LLINT_THROW(createSyntaxError(exec, regExp->errorMessage()));
LLINT_RETURN(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regExp));
}
Added: trunk/Source/_javascript_Core/tests/stress/regress-159954.js (0 => 203452)
--- trunk/Source/_javascript_Core/tests/stress/regress-159954.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/regress-159954.js 2016-07-20 14:50:32 UTC (rev 203452)
@@ -0,0 +1,23 @@
+// Regression test for 159954. This test should not crash or throw an exception.
+
+function testRegExp(regexpExpression)
+{
+ try {
+ let result = eval(regexpExpression);
+
+ throw "Expected \"" + regexpExpression + "\" to throw and it didn't";
+ } catch (e) {
+ if (e != "SyntaxError: Invalid regular _expression_: pattern exceeds string length limits")
+ throw e;
+ return true;
+ }
+}
+
+testRegExp("/a{2147483649,2147483650}a{2147483649,2147483650}/.exec('aaaa')");
+testRegExp("/a{2147483649,2147483650}a{2147483649,2147483650}/.exec('aa')");
+testRegExp("/(?:\1{2147483649,2147483650})+/.exec('123')");
+testRegExp("/([^]{2147483648,2147483651}(?:.){2})+?/.exec('xxx')");
+testRegExp("/(\u0004\W\u0f0b+?$[\xa7\t\t-\ue118\f]{2147483648,2147483648})+.+?/u.exec('testing')");
+testRegExp("/(.{2147483649,2147483652})+?/g.exec('xxx')");
+testRegExp("/(?:(?:[\D]{2147483649})+?.)*?/igmy.exec('123\\n123')");
+testRegExp("/(?:\1{2147483648,})+?/m.exec('xxx')");
Modified: trunk/Source/_javascript_Core/yarr/YarrParser.h (203451 => 203452)
--- trunk/Source/_javascript_Core/yarr/YarrParser.h 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/yarr/YarrParser.h 2016-07-20 14:50:32 UTC (rev 203452)
@@ -32,8 +32,6 @@
namespace JSC { namespace Yarr {
-#define REGEXP_ERROR_PREFIX "Invalid regular _expression_: "
-
enum BuiltInCharacterClassID {
DigitClassID,
SpaceClassID,
@@ -48,23 +46,6 @@
template<class FriendDelegate>
friend const char* parse(FriendDelegate&, const String& pattern, bool isUnicode, unsigned backReferenceLimit);
- enum ErrorCode {
- NoError,
- PatternTooLarge,
- QuantifierOutOfOrder,
- QuantifierWithoutAtom,
- QuantifierTooLarge,
- MissingParentheses,
- ParenthesesUnmatched,
- ParenthesesTypeInvalid,
- CharacterClassUnmatched,
- CharacterClassOutOfOrder,
- EscapeUnterminated,
- InvalidUnicodeEscape,
- InvalidIdentityEscape,
- NumberOfErrorCodes
- };
-
/*
* CharacterClassParserDelegate:
*
@@ -76,7 +57,7 @@
*/
class CharacterClassParserDelegate {
public:
- CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err)
+ CharacterClassParserDelegate(Delegate& delegate, YarrPattern::ErrorCode& err)
: m_delegate(delegate)
, m_err(err)
, m_state(Empty)
@@ -138,7 +119,7 @@
case CachedCharacterHyphen:
if (ch < m_character) {
- m_err = CharacterClassOutOfOrder;
+ m_err = YarrPattern::CharacterClassOutOfOrder;
return;
}
m_delegate.atomCharacterClassRange(m_character, ch);
@@ -219,7 +200,7 @@
private:
Delegate& m_delegate;
- ErrorCode& m_err;
+ YarrPattern::ErrorCode& m_err;
enum CharacterClassConstructionState {
Empty,
CachedCharacter,
@@ -233,7 +214,7 @@
Parser(Delegate& delegate, const String& pattern, bool isUnicode, unsigned backReferenceLimit)
: m_delegate(delegate)
, m_backReferenceLimit(backReferenceLimit)
- , m_err(NoError)
+ , m_err(YarrPattern::NoError)
, m_data(pattern.characters<CharType>())
, m_size(pattern.length())
, m_index(0)
@@ -248,7 +229,7 @@
bool isIdentityEscapeAnError(int ch)
{
if (m_isUnicode && !strchr("^$\\.*+?()[]{}|/", ch)) {
- m_err = InvalidIdentityEscape;
+ m_err = YarrPattern::InvalidIdentityEscape;
return true;
}
@@ -283,7 +264,7 @@
consume();
if (atEndOfPattern()) {
- m_err = EscapeUnterminated;
+ m_err = YarrPattern::EscapeUnterminated;
return false;
}
@@ -449,7 +430,7 @@
UChar32 codePoint = 0;
do {
if (atEndOfPattern() || !isASCIIHexDigit(peek())) {
- m_err = InvalidUnicodeEscape;
+ m_err = YarrPattern::InvalidUnicodeEscape;
break;
}
@@ -456,12 +437,12 @@
codePoint = (codePoint << 4) | toASCIIHexValue(consume());
if (codePoint > UCHAR_MAX_VALUE)
- m_err = InvalidUnicodeEscape;
+ m_err = YarrPattern::InvalidUnicodeEscape;
} while (!atEndOfPattern() && peek() != '}');
if (!atEndOfPattern() && peek() == '}')
consume();
else if (!m_err)
- m_err = InvalidUnicodeEscape;
+ m_err = YarrPattern::InvalidUnicodeEscape;
if (m_err)
return false;
@@ -581,7 +562,7 @@
return;
}
- m_err = CharacterClassUnmatched;
+ m_err = YarrPattern::CharacterClassUnmatched;
}
/*
@@ -597,7 +578,7 @@
if (tryConsume('?')) {
if (atEndOfPattern()) {
- m_err = ParenthesesTypeInvalid;
+ m_err = YarrPattern::ParenthesesTypeInvalid;
return;
}
@@ -615,7 +596,7 @@
break;
default:
- m_err = ParenthesesTypeInvalid;
+ m_err = YarrPattern::ParenthesesTypeInvalid;
}
} else
m_delegate.atomParenthesesSubpatternBegin();
@@ -637,7 +618,7 @@
if (m_parenthesesNestingDepth > 0)
m_delegate.atomParenthesesEnd();
else
- m_err = ParenthesesUnmatched;
+ m_err = YarrPattern::ParenthesesUnmatched;
--m_parenthesesNestingDepth;
}
@@ -653,7 +634,7 @@
ASSERT(min <= max);
if (min == UINT_MAX) {
- m_err = QuantifierTooLarge;
+ m_err = YarrPattern::QuantifierTooLarge;
return;
}
@@ -660,7 +641,7 @@
if (lastTokenWasAnAtom)
m_delegate.quantifyAtom(min, max, !tryConsume('?'));
else
- m_err = QuantifierWithoutAtom;
+ m_err = YarrPattern::QuantifierWithoutAtom;
}
/*
@@ -754,7 +735,7 @@
if (min <= max)
parseQuantifier(lastTokenWasAnAtom, min, max);
else
- m_err = QuantifierOutOfOrder;
+ m_err = YarrPattern::QuantifierOutOfOrder;
lastTokenWasAnAtom = false;
break;
}
@@ -775,7 +756,7 @@
}
if (m_parenthesesNestingDepth > 0)
- m_err = MissingParentheses;
+ m_err = YarrPattern::MissingParentheses;
}
/*
@@ -787,29 +768,12 @@
const char* parse()
{
if (m_size > MAX_PATTERN_SIZE)
- m_err = PatternTooLarge;
+ m_err = YarrPattern::PatternTooLarge;
else
parseTokens();
ASSERT(atEndOfPattern() || m_err);
-
- // The order of this array must match the ErrorCode enum.
- static const char* errorMessages[NumberOfErrorCodes] = {
- 0, // NoError
- REGEXP_ERROR_PREFIX "regular _expression_ too large",
- REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier",
- REGEXP_ERROR_PREFIX "nothing to repeat",
- REGEXP_ERROR_PREFIX "number too large in {} quantifier",
- REGEXP_ERROR_PREFIX "missing )",
- REGEXP_ERROR_PREFIX "unmatched parentheses",
- REGEXP_ERROR_PREFIX "unrecognized character after (?",
- REGEXP_ERROR_PREFIX "missing terminating ] for character class",
- REGEXP_ERROR_PREFIX "range out of order in character class",
- REGEXP_ERROR_PREFIX "\\ at end of pattern",
- REGEXP_ERROR_PREFIX "invalid unicode {} escape",
- REGEXP_ERROR_PREFIX "invalid escaped character for unicode pattern"
- };
-
- return errorMessages[m_err];
+
+ return YarrPattern::errorMessage(m_err);
}
// Misc helper functions:
@@ -913,7 +877,7 @@
Delegate& m_delegate;
unsigned m_backReferenceLimit;
- ErrorCode m_err;
+ YarrPattern::ErrorCode m_err;
const CharType* m_data;
unsigned m_size;
unsigned m_index;
Modified: trunk/Source/_javascript_Core/yarr/YarrPattern.cpp (203451 => 203452)
--- trunk/Source/_javascript_Core/yarr/YarrPattern.cpp 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/yarr/YarrPattern.cpp 2016-07-20 14:50:32 UTC (rev 203452)
@@ -578,13 +578,14 @@
m_alternative = m_alternative->m_parent->addNewAlternative();
}
- bool setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned& newCallFrameSize) WARN_UNUSED_RETURN
+ YarrPattern::ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned& newCallFrameSize) WARN_UNUSED_RETURN
{
if (UNLIKELY(!isSafeToRecurse()))
- return false;
+ return YarrPattern::TooManyDisjunctions;
+ YarrPattern::ErrorCode error = YarrPattern::NoError;
alternative->m_hasFixedSize = true;
- Checked<unsigned> currentInputPosition = initialInputPosition;
+ Checked<unsigned, RecordOverflow> currentInputPosition = initialInputPosition;
for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
PatternTerm& term = alternative->m_terms[i];
@@ -639,8 +640,9 @@
if (term.quantityCount == 1 && !term.parentheses.isCopy) {
if (term.quantityType != QuantifierFixedCount)
currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- if (!setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize))
- return false;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (error)
+ return error;
// If quantity is fixed, then pre-check its minimum size.
if (term.quantityType == QuantifierFixedCount)
currentInputPosition += term.parentheses.disjunction->m_minimumSize;
@@ -647,14 +649,16 @@
term.inputPosition = currentInputPosition.unsafeGet();
} else if (term.parentheses.isTerminal) {
currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
- if (!setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize))
- return false;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (error)
+ return error;
term.inputPosition = currentInputPosition.unsafeGet();
} else {
term.inputPosition = currentInputPosition.unsafeGet();
unsigned ignoredCallFrameSize;
- if (!setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet(), ignoredCallFrameSize))
- return false;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet(), ignoredCallFrameSize);
+ if (error)
+ return error;
currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses;
}
// Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length.
@@ -664,8 +668,9 @@
case PatternTerm::TypeParentheticalAssertion:
term.inputPosition = currentInputPosition.unsafeGet();
term.frameLocation = currentCallFrameSize;
- if (!setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet(), currentCallFrameSize))
- return false;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (error)
+ return error;
break;
case PatternTerm::TypeDotStarEnclosure:
@@ -673,17 +678,19 @@
term.inputPosition = initialInputPosition;
break;
}
+ if (currentInputPosition.hasOverflowed())
+ return YarrPattern::OffsetTooLarge;
}
alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet();
newCallFrameSize = currentCallFrameSize;
- return true;
+ return error;
}
- bool setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned& callFrameSize) WARN_UNUSED_RETURN
+ YarrPattern::ErrorCode setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned& callFrameSize)
{
if (UNLIKELY(!isSafeToRecurse()))
- return false;
+ return YarrPattern::TooManyDisjunctions;
if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1))
initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative;
@@ -691,12 +698,14 @@
unsigned minimumInputSize = UINT_MAX;
unsigned maximumCallFrameSize = 0;
bool hasFixedSize = true;
+ YarrPattern::ErrorCode error = YarrPattern::NoError;
for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
PatternAlternative* alternative = disjunction->m_alternatives[alt].get();
unsigned currentAlternativeCallFrameSize;
- if (!setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition, currentAlternativeCallFrameSize))
- return false;
+ error = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition, currentAlternativeCallFrameSize);
+ if (error)
+ return error;
minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize);
maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize);
hasFixedSize &= alternative->m_hasFixedSize;
@@ -711,7 +720,7 @@
disjunction->m_minimumSize = minimumInputSize;
disjunction->m_callFrameSize = maximumCallFrameSize;
callFrameSize = maximumCallFrameSize;
- return true;
+ return error;
}
const char* setupOffsets()
@@ -718,8 +727,9 @@
{
// FIXME: Yarr should not use the stack to handle subpatterns (rdar://problem/26436314).
unsigned ignoredCallFrameSize;
- if (!setupDisjunctionOffsets(m_pattern.m_body, 0, 0, ignoredCallFrameSize))
- return REGEXP_ERROR_PREFIX "too many nested disjunctions";
+ YarrPattern::ErrorCode error = setupDisjunctionOffsets(m_pattern.m_body, 0, 0, ignoredCallFrameSize);
+ if (error)
+ return YarrPattern::errorMessage(error);
return nullptr;
}
@@ -878,6 +888,31 @@
bool m_invertParentheticalAssertion;
};
+const char* YarrPattern::errorMessage(YarrPattern::ErrorCode error)
+{
+#define REGEXP_ERROR_PREFIX "Invalid regular _expression_: "
+ // The order of this array must match the ErrorCode enum.
+ static const char* errorMessages[NumberOfErrorCodes] = {
+ nullptr, // NoError
+ REGEXP_ERROR_PREFIX "regular _expression_ too large",
+ REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier",
+ REGEXP_ERROR_PREFIX "nothing to repeat",
+ REGEXP_ERROR_PREFIX "number too large in {} quantifier",
+ REGEXP_ERROR_PREFIX "missing )",
+ REGEXP_ERROR_PREFIX "unmatched parentheses",
+ REGEXP_ERROR_PREFIX "unrecognized character after (?",
+ REGEXP_ERROR_PREFIX "missing terminating ] for character class",
+ REGEXP_ERROR_PREFIX "range out of order in character class",
+ REGEXP_ERROR_PREFIX "\\ at end of pattern",
+ REGEXP_ERROR_PREFIX "invalid unicode {} escape",
+ REGEXP_ERROR_PREFIX "invalid escaped character for unicode pattern",
+ REGEXP_ERROR_PREFIX "too many nested disjunctions",
+ REGEXP_ERROR_PREFIX "pattern exceeds string length limits"
+ };
+
+ return errorMessages[error];
+}
+
const char* YarrPattern::compile(const String& patternString, void* stackLimit)
{
YarrPatternConstructor constructor(*this, stackLimit);
Modified: trunk/Source/_javascript_Core/yarr/YarrPattern.h (203451 => 203452)
--- trunk/Source/_javascript_Core/yarr/YarrPattern.h 2016-07-20 13:07:05 UTC (rev 203451)
+++ trunk/Source/_javascript_Core/yarr/YarrPattern.h 2016-07-20 14:50:32 UTC (rev 203452)
@@ -306,6 +306,27 @@
struct YarrPattern {
JS_EXPORT_PRIVATE YarrPattern(const String& pattern, RegExpFlags, const char** error, void* stackLimit = nullptr);
+ enum ErrorCode {
+ NoError,
+ PatternTooLarge,
+ QuantifierOutOfOrder,
+ QuantifierWithoutAtom,
+ QuantifierTooLarge,
+ MissingParentheses,
+ ParenthesesUnmatched,
+ ParenthesesTypeInvalid,
+ CharacterClassUnmatched,
+ CharacterClassOutOfOrder,
+ EscapeUnterminated,
+ InvalidUnicodeEscape,
+ InvalidIdentityEscape,
+ TooManyDisjunctions,
+ OffsetTooLarge,
+ NumberOfErrorCodes
+ };
+
+ WTF_EXPORT_PRIVATE static const char* errorMessage(ErrorCode);
+
void reset()
{
m_numSubpatterns = 0;