ICU-20037 Fixing ScientificMatcher integer overflow. (#138)

Also restricts parsing to read only one exponent per string.
This commit is contained in:
Shane 2018-09-18 18:10:38 -07:00 committed by Shane Carr
parent cc6c31432e
commit a8a6ffdb92
No known key found for this signature in database
GPG Key ID: FCED3B24AAB18B5C
5 changed files with 56 additions and 0 deletions

View File

@ -56,6 +56,11 @@ bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErr
return false;
}
// Only accept one exponent per string.
if (0 != (result.flags & FLAG_HAS_EXPONENT)) {
return false;
}
// First match the scientific separator, and then match another number after it.
// NOTE: This is guarded by the smoke test; no need to check fExponentSeparatorString length again.
int overlap1 = segment.getCommonPrefixLength(fExponentSeparatorString);

View File

@ -215,6 +215,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(Test13763_FieldPositionIteratorOffset);
TESTCASE_AUTO(Test13777_ParseLongNameNonCurrencyMode);
TESTCASE_AUTO(Test13804_EmptyStringsWhenParsing);
TESTCASE_AUTO(Test20037_ScientificIntegerOverflow);
TESTCASE_AUTO(Test13840_ParseLongStringCrash);
TESTCASE_AUTO(Test13850_EmptyStringCurrency);
TESTCASE_AUTO_END;
@ -9185,6 +9186,28 @@ void NumberFormatTest::Test13804_EmptyStringsWhenParsing() {
}
}
void NumberFormatTest::Test20037_ScientificIntegerOverflow() {
IcuTestErrorCode status(*this, "Test20037_ScientificIntegerOverflow");
LocalPointer<NumberFormat> nf(NumberFormat::createInstance(status));
Formattable result;
// Test overflow of exponent
nf->parse(u"1E-2147483648", result, status);
StringPiece sp = result.getDecimalNumber(status);
assertEquals(u"Should snap to zero",
u"0",
{sp.data(), sp.length(), US_INV});
// Test edge case overflow of exponent
result = Formattable();
nf->parse(u"1E-2147483647E-1", result, status);
sp = result.getDecimalNumber(status);
assertEquals(u"Should not overflow and should parse only the first exponent",
u"1E-2147483647",
{sp.data(), sp.length(), US_INV});
}
void NumberFormatTest::Test13840_ParseLongStringCrash() {
IcuTestErrorCode status(*this, "Test13840_ParseLongStringCrash");

View File

@ -279,6 +279,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void Test13763_FieldPositionIteratorOffset();
void Test13777_ParseLongNameNonCurrencyMode();
void Test13804_EmptyStringsWhenParsing();
void Test20037_ScientificIntegerOverflow();
void Test13840_ParseLongStringCrash();
void Test13850_EmptyStringCurrency();

View File

@ -54,6 +54,11 @@ public class ScientificMatcher implements NumberParseMatcher {
return false;
}
// Only accept one exponent per string.
if (0 != (result.flags & ParsedNumber.FLAG_HAS_EXPONENT)) {
return false;
}
// First match the scientific separator, and then match another number after it.
// NOTE: This is guarded by the smoke test; no need to check exponentSeparatorString length again.
int overlap1 = segment.getCommonPrefixLength(exponentSeparatorString);

View File

@ -6294,6 +6294,28 @@ public class NumberFormatTest extends TestFmwk {
}
}
@Test
public void Test20037_ScientificIntegerOverflow() throws ParseException {
NumberFormat nf = NumberFormat.getInstance(ULocale.US);
// Test overflow of exponent
Number result = nf.parse("1E-2147483648");
assertEquals("Should snap to zero",
"0", result.toString());
// Test edge case overflow of exponent
// Note: the behavior is different from C++; this is probably due to the
// intermediate BigDecimal form, which has its own restrictions
result = nf.parse("1E-2147483647E-1");
assertEquals("Should not overflow and should parse only the first exponent",
"0.0", result.toString());
// For Java, we should get *pretty close* to 2^31.
result = nf.parse("1E-547483647");
assertEquals("Should *not* snap to zero",
"1E-547483647", result.toString());
}
@Test
public void test13840_ParseLongStringCrash() throws ParseException {
NumberFormat nf = NumberFormat.getInstance(ULocale.ENGLISH);