Endless loop in CFGAS_FormatString::FormatStrNum(). Comparison didn't take sign into account, nor the possibility of CFGAS_Decimal overflowing to 0 (fixing that should be a separate issue). Fix another obvious overflow while computing threshold. Bug: pdfium:1244 Change-Id: Idbbfd311a60b57f83b18ee2fa8af45eeafa877f8 Reviewed-on: https://pdfium-review.googlesource.com/c/50894 Reviewed-by: Lei Zhang <thestig@chromium.org> Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/xfa/fgas/crt/cfgas_decimal.h b/xfa/fgas/crt/cfgas_decimal.h index ddfd8e8..4751c5b 100644 --- a/xfa/fgas/crt/cfgas_decimal.h +++ b/xfa/fgas/crt/cfgas_decimal.h
@@ -25,6 +25,7 @@ CFGAS_Decimal operator*(const CFGAS_Decimal& val) const; CFGAS_Decimal operator/(const CFGAS_Decimal& val) const; + bool IsNotZero() const { return m_uHi || m_uMid || m_uLo; } uint8_t GetScale() const { return m_uScale; } void SetScale(uint8_t newScale); void SetNegate(); @@ -35,7 +36,6 @@ uint32_t lo, bool neg, uint8_t scale); - bool IsNotZero() const { return m_uHi || m_uMid || m_uLo; } uint32_t m_uHi = 0; uint32_t m_uMid = 0;
diff --git a/xfa/fgas/crt/cfgas_formatstring.cpp b/xfa/fgas/crt/cfgas_formatstring.cpp index 7cc6d93..45e0445 100644 --- a/xfa/fgas/crt/cfgas_formatstring.cpp +++ b/xfa/fgas/crt/cfgas_formatstring.cpp
@@ -11,6 +11,7 @@ #include <vector> #include "core/fxcrt/fx_extension.h" +#include "core/fxcrt/fx_safe_types.h" #include "xfa/fgas/crt/cfgas_decimal.h" #define FX_LOCALECATEGORY_DateHash 0xbde9abde @@ -1896,25 +1897,30 @@ } } - int threshold = 1; + FX_SAFE_UINT32 threshold = 1; while (fixed_count > 1) { threshold *= 10; fixed_count--; } - if (decimal.ToDouble() != 0.0) { - if (decimal.ToDouble() < threshold) { - decimal = decimal * CFGAS_Decimal(10); - exponent = -1; - while (decimal.ToDouble() < threshold) { - decimal = decimal * CFGAS_Decimal(10); - exponent -= 1; - } - } else if (decimal.ToDouble() > threshold) { - threshold *= 10; - while (decimal.ToDouble() > threshold) { - decimal = decimal / CFGAS_Decimal(10); - exponent += 1; - } + if (!threshold.IsValid()) + return false; + + bool bAdjusted = false; + while (decimal.IsNotZero() && + fabs(decimal.ToDouble()) < threshold.ValueOrDie()) { + decimal = decimal * CFGAS_Decimal(10); + --exponent; + bAdjusted = true; + } + if (!bAdjusted) { + threshold *= 10; + if (!threshold.IsValid()) + return false; + + while (decimal.IsNotZero() && + fabs(decimal.ToDouble()) > threshold.ValueOrDie()) { + decimal = decimal / CFGAS_Decimal(10); + ++exponent; } } } @@ -2077,14 +2083,12 @@ } if (bNeg) *wsOutput = pLocale->GetMinusSymbol() + *wsOutput; - - return false; + return true; } if (dot_index_f == pdfium::base::checked_cast<int32_t>(wsNumFormat.GetLength())) { if (!bAddNeg && bNeg) *wsOutput = pLocale->GetMinusSymbol() + *wsOutput; - return true; }
diff --git a/xfa/fgas/crt/cfgas_formatstring_unittest.cpp b/xfa/fgas/crt/cfgas_formatstring_unittest.cpp index 8bc77a5..a3b7bd4 100644 --- a/xfa/fgas/crt/cfgas_formatstring_unittest.cpp +++ b/xfa/fgas/crt/cfgas_formatstring_unittest.cpp
@@ -303,12 +303,14 @@ } TEST_F(CFGAS_FormatStringTest, NumParse) { - struct { + struct TestCase { const wchar_t* locale; const wchar_t* input; const wchar_t* pattern; const wchar_t* output; - } tests[] = { + }; + + static const TestCase tests[] = { // {L"en", L"€100.00", L"num(en_GB){$z,zz9.99}", L"100"}, // {L"en", L"1050", L"99V99", L"10.50"}, // {L"en", L"3125", L"99V99", L"31.25"}, @@ -415,22 +417,24 @@ {L"en", L"123.545,4", L"zzz.zzz,z", L"123.5454"}, }; - for (size_t i = 0; i < FX_ArraySize(tests); ++i) { + for (const auto& test : tests) { WideString result; - EXPECT_TRUE(fmt(tests[i].locale) - ->ParseNum(tests[i].input, tests[i].pattern, &result)) - << " TEST: " << i; - EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i; + EXPECT_TRUE(fmt(test.locale)->ParseNum(test.input, test.pattern, &result)) + << " TEST: " << test.input << ", " << test.pattern; + EXPECT_STREQ(test.output, result.c_str()) + << " TEST: " << test.input << ", " << test.pattern; } } TEST_F(CFGAS_FormatStringTest, NumFormat) { - struct { + struct TestCase { const wchar_t* locale; const wchar_t* input; const wchar_t* pattern; const wchar_t* output; - } tests[] = { + }; + + static const TestCase tests[] = { {L"en", L"1.234", L"zz9.zzz", L"1.234"}, {L"en", L"1", L"num{z 'text'}", L"1 text"}, {L"en", L"1", L"num{'text' z}", L"text 1"}, @@ -518,14 +522,21 @@ {L"en", L"1", L"9.C", L"1"}, {L"en", L"1", L"9.d", L"1"}, {L"en", L"1", L"9.D", L"1"}, + // https://crbug.com/pdfium/1244 + {L"en", L"1", L"E", L"1"}, + {L"en", L"0", L"E", L"0"}, + {L"en", L"-1", L"E", L"-1"}, + {L"en", L"900000000000000000000", L"E", L"900,000,000,000,000,000,000"}, + // TODO(tsepez): next one seems wrong + // {L"en", L".000000000000000000009", L"E", L"9"}, }; - for (size_t i = 0; i < FX_ArraySize(tests); ++i) { + for (const auto& test : tests) { WideString result; - EXPECT_TRUE(fmt(tests[i].locale) - ->FormatNum(tests[i].input, tests[i].pattern, &result)) - << " TEST: " << i; - EXPECT_STREQ(tests[i].output, result.c_str()) << " TEST: " << i; + EXPECT_TRUE(fmt(test.locale)->FormatNum(test.input, test.pattern, &result)) + << " TEST: " << test.input << ", " << test.pattern; + EXPECT_STREQ(test.output, result.c_str()) + << " TEST: " << test.input << ", " << test.pattern; } }