Commit message: ICU-20246 Fixing another integer overflow in number parsing.
CVE: CVE-2018-18928
CWE: CWE-190
Vulnerability description: International Components for Unicode (ICU) for C/C++ 63.1 has an integer overflow in number::impl::DecimalQuantity::toScientificString() in i18n/number_decimalquantity.cpp.

Project: icu
File: numfmtst.cpp

void NumberFormatTest::TestFastFormatInt32() {
    IcuTestErrorCode status(*this, "TestFastFormatInt32");

    // The two simplest formatters, old API and new API.
    // Old API should use the fastpath for ints.
    LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en");
    LocalPointer<NumberFormat> df(NumberFormat::createInstance("en", status), status);
    if (!assertSuccess("", status, true, __FILE__, __LINE__)) {return;}

    double nums[] = {
            0.0,
            -0.0,
            NAN,
            INFINITY,
            0.1,
            1.0,
            1.1,
            2.0,
            3.0,
            9.0,
            10.0,
            99.0,
            100.0,
            999.0,
            1000.0,
            9999.0,
            10000.0,
            99999.0,
            100000.0,
            999999.0,
            1000000.0,
            static_cast<double>(INT32_MAX) - 1,
            static_cast<double>(INT32_MAX),
            static_cast<double>(INT32_MAX) + 1,
            static_cast<double>(INT32_MIN) - 1,
            static_cast<double>(INT32_MIN),
            static_cast<double>(INT32_MIN) + 1};

    for (auto num : nums) {
        UnicodeString expected = lnf.formatDouble(num, status).toString();
        UnicodeString actual;
        df->format(num, actual);
        assertEquals(UnicodeString("d = ") + num, expected, actual);
    }
}

Project: icu
File: fmtable.cpp

void  Formattable::init() {
    fValue.fInt64 = 0;
    fType = kLong;
    fDecimalStr = NULL;
    fDecimalQuantity = NULL;
    fBogus.setToBogus(); 
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::Test12567() {
    IcuTestErrorCode errorCode(*this, "Test12567");
    // Ticket #12567: DecimalFormat.equals() may not be symmetric
    LocalPointer<DecimalFormat> df1((DecimalFormat *)
        NumberFormat::createInstance(Locale::getUS(), UNUM_CURRENCY, errorCode));
    LocalPointer<DecimalFormat> df2((DecimalFormat *)
        NumberFormat::createInstance(Locale::getUS(), UNUM_DECIMAL, errorCode));
    if (!assertSuccess("", errorCode, true, __FILE__, __LINE__)) { return; }
    // NOTE: CurrencyPluralInfo equality not tested in C++ because its operator== is not defined.
    df1->applyPattern(u"0.00", errorCode);
    df2->applyPattern(u"0.00", errorCode);
    assertTrue("df1 == df2", *df1 == *df2);
    assertTrue("df2 == df1", *df2 == *df1);
    df2->setPositivePrefix(u"abc");
    assertTrue("df1 != df2", *df1 != *df2);
    assertTrue("df2 != df1", *df2 != *df1);
}

Project: icu
File: fmtable.cpp

static Formattable* createArrayCopy(const Formattable* array, int32_t count) {
    Formattable *result = new Formattable[count];
    if (result != NULL) {
        for (int32_t i=0; i<count; ++i)
            result[i] = array[i]; // Don't memcpy!
    }
    return result;
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::TestBenchmark() {
/*
    UErrorCode status = U_ZERO_ERROR;
    Locale en("en");
    DecimalFormatSymbols sym(en, status);
    DecimalFormat fmt("0.0000000", new DecimalFormatSymbols(sym), status);
//    DecimalFormat fmt("0.00000E0", new DecimalFormatSymbols(sym), status);
//    DecimalFormat fmt("0", new DecimalFormatSymbols(sym), status);
    FieldPosition fpos(FieldPosition::DONT_CARE);
    clock_t start = clock();
    for (int32_t i = 0; i < 1000000; ++i) {
        UnicodeString append;
        fmt.format(3.0, append, fpos, status);
//        fmt.format(4.6692016, append, fpos, status);
//        fmt.format(1234567.8901, append, fpos, status);
//        fmt.format(2.99792458E8, append, fpos, status);
//        fmt.format(31, append);
    }
    errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
    assertSuccess("", status);

    UErrorCode status = U_ZERO_ERROR;
    MessageFormat fmt("{0, plural, one {I have # friend.} other {I have # friends.}}", status);
    FieldPosition fpos(FieldPosition::DONT_CARE);
    Formattable one(1.0);
    Formattable three(3.0);
    clock_t start = clock();
    for (int32_t i = 0; i < 500000; ++i) {
        UnicodeString append;
        fmt.format(&one, 1, append, fpos, status);
        UnicodeString append2;
        fmt.format(&three, 1, append2, fpos, status);
    }
    errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
    assertSuccess("", status);

    UErrorCode status = U_ZERO_ERROR;
    Locale en("en");
    Measure measureC(23, MeasureUnit::createCelsius(status), status);
    MeasureFormat fmt(en, UMEASFMT_WIDTH_WIDE, status);
    FieldPosition fpos(FieldPosition::DONT_CARE);
    clock_t start = clock();
    for (int32_t i = 0; i < 1000000; ++i) {
        UnicodeString appendTo;
        fmt.formatMeasures(
                &measureC, 1, appendTo, fpos, status);
    }
    errln("Took %f", (double) (clock() - start) / CLOCKS_PER_SEC);
    assertSuccess("", status);
*/
}

Project: icu
File: numfmtst.cpp

 void NumberFormatTest::Test11647_PatternCurrencySymbols() {
    UErrorCode status = U_ZERO_ERROR;
    DecimalFormat df(status);
    df.applyPattern(u"¤¤¤¤#", status);
    if (!assertSuccess("", status)) return;
    UnicodeString actual;
    df.format(123, actual);
    assertEquals("Should replace 4 currency signs with U+FFFD", u"\uFFFD123", actual);
}

Project: icu
File: numfmtst.cpp

UBool NumberFormatTest::equalValue(const Formattable& a, const Formattable& b) {
    if (a.getType() == b.getType()) {
        return a == b;
    }

    if (a.getType() == Formattable::kLong) {
        if (b.getType() == Formattable::kInt64) {
            return a.getLong() == b.getLong();
        } else if (b.getType() == Formattable::kDouble) {
            return (double) a.getLong() == b.getDouble(); // TODO check use of double instead of long
        }
    } else if (a.getType() == Formattable::kDouble) {
        if (b.getType() == Formattable::kLong) {
            return a.getDouble() == (double) b.getLong();
        } else if (b.getType() == Formattable::kInt64) {
            return a.getDouble() == (double)b.getInt64();
        }
    } else if (a.getType() == Formattable::kInt64) {
        if (b.getType() == Formattable::kLong) {
                return a.getInt64() == (int64_t)b.getLong();
        } else if (b.getType() == Formattable::kDouble) {
            return a.getInt64() == (int64_t)b.getDouble();
        }
    }
    return FALSE;
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::Test11020_RoundingInScientificNotation() {
    UErrorCode status = U_ZERO_ERROR;
    DecimalFormatSymbols sym(Locale::getFrance(), status);
    DecimalFormat fmt(u"0.05E0", sym, status);
    if (!assertSuccess("", status, true, __FILE__, __LINE__)) { return; }
    assertSuccess("", status);
    UnicodeString result;
    fmt.format(12301.2, result);
    assertEquals("Rounding increment should be applied after magnitude scaling", u"1,25E4", result);
}

Project: icu
File: fmtable.cpp

Formattable::clone() const {
    return new Formattable(*this);
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::expectCurrency(NumberFormat& nf, const Locale& locale,
                                      double value, const UnicodeString& string) {
    UErrorCode ec = U_ZERO_ERROR;
    DecimalFormat& fmt = * (DecimalFormat*) &nf;
    const UChar DEFAULT_CURR[] = {45/*-*/,0};
    UChar curr[4];
    u_strcpy(curr, DEFAULT_CURR);
    if (*locale.getLanguage() != 0) {
        ucurr_forLocale(locale.getName(), curr, 4, &ec);
        assertSuccess("ucurr_forLocale", ec);
        fmt.setCurrency(curr, ec);
        assertSuccess("DecimalFormat::setCurrency", ec);
        fmt.setCurrency(curr); //Deprecated variant, for coverage only
    }
    UnicodeString s;
    fmt.format(value, s);
    s.findAndReplace((UChar32)0x00A0, (UChar32)0x0020);

    // Default display of the number yields "1234.5599999999999"
    // instead of "1234.56".  Use a formatter to fix this.
    NumberFormat* f =
        NumberFormat::createInstance(Locale::getUS(), ec);
    UnicodeString v;
    if (U_FAILURE(ec)) {
        // Oops; bad formatter.  Use default op+= display.
        v = (UnicodeString)"" + value;
    } else {
        f->setMaximumFractionDigits(4);
        f->setGroupingUsed(FALSE);
        f->format(value, v);
    }
    delete f;

    if (s == string) {
        logln((UnicodeString)"Ok: " + v + " x " + curr + " => " + prettify(s));
    } else {
        errln((UnicodeString)"FAIL: " + v + " x " + curr + " => " + prettify(s) +
              ", expected " + prettify(string));
    }
}

Project: icu
File: fmtable.cpp

Formattable::Formattable(UObject* objectToAdopt)
{
    init();
    fType = kObject;
    fValue.fObject = objectToAdopt;
}

Project: icu
File: number_decimalquantity.cpp

void DecimalQuantity::moveBcdFrom(DecimalQuantity &other) {
    setBcdToZero();
    if (other.usingBytes) {
        usingBytes = true;
        fBCD.bcdBytes.ptr = other.fBCD.bcdBytes.ptr;
        fBCD.bcdBytes.len = other.fBCD.bcdBytes.len;
        // Take ownership away from the old instance:
        other.fBCD.bcdBytes.ptr = nullptr;
        other.usingBytes = false;
    } else {
        fBCD.bcdLong = other.fBCD.bcdLong;
    }
}

Project: icu
File: number_decimalquantity.cpp

void DecimalQuantity::readDoubleConversionToBcd(
        const char* buffer, int32_t length, int32_t point) {
    // NOTE: Despite the fact that double-conversion's API is called
    // "DoubleToAscii", they actually use '0' (as opposed to u8'0').
    if (length > 16) {
        ensureCapacity(length);
        for (int32_t i = 0; i < length; i++) {
            fBCD.bcdBytes.ptr[i] = buffer[length-i-1] - '0';
        }
    } else {
        uint64_t result = 0L;
        for (int32_t i = 0; i < length; i++) {
            result |= static_cast<uint64_t>(buffer[length-i-1] - '0') << (4 * i);
        }
        fBCD.bcdLong = result;
    }
    scale = point - length;
    precision = length;
}

Project: icu
File: numfmtst.cpp

NumberFormatTest::TestExponential(void)
{
    UErrorCode status = U_ZERO_ERROR;
    DecimalFormatSymbols sym(Locale::getUS(), status);
    if (U_FAILURE(status)) { errcheckln(status, "FAIL: Bad status returned by DecimalFormatSymbols ct - %s", u_errorName(status)); return; }
    const char* pat[] = { "0.####E0", "00.000E00", "##0.######E000", "0.###E0;[0.###E0]"  };
    int32_t pat_length = UPRV_LENGTHOF(pat);

// The following #if statements allow this test to be built and run on
// platforms that do not have standard IEEE numerics.  For example,
// S/390 doubles have an exponent range of -78 to +75.  For the
// following #if statements to work, float.h must define
// DBL_MAX_10_EXP to be a compile-time constant.

// This section may be expanded as needed.

#if DBL_MAX_10_EXP > 300
    double val[] = { 0.01234, 123456789, 1.23e300, -3.141592653e-271 };
    int32_t val_length = UPRV_LENGTHOF(val);
    const char* valFormat[] =
    {
        // 0.####E0
        "1.234E-2", "1.2346E8", "1.23E300", "-3.1416E-271",
        // 00.000E00
        "12.340E-03", "12.346E07", "12.300E299", "-31.416E-272",
        // ##0.######E000
        "12.34E-003", "123.4568E006", "1.23E300", "-314.1593E-273",
        // 0.###E0;[0.###E0]
        "1.234E-2", "1.235E8", "1.23E300", "[3.142E-271]"
    };
    double valParse[] =
    {
        0.01234, 123460000, 1.23E300, -3.1416E-271,
        0.01234, 123460000, 1.23E300, -3.1416E-271,
        0.01234, 123456800, 1.23E300, -3.141593E-271,
        0.01234, 123500000, 1.23E300, -3.142E-271,
    };
#elif DBL_MAX_10_EXP > 70
    double val[] = { 0.01234, 123456789, 1.23e70, -3.141592653e-71 };
    int32_t val_length = UPRV_LENGTHOF(val);
    char* valFormat[] =
    {
        // 0.####E0
        "1.234E-2", "1.2346E8", "1.23E70", "-3.1416E-71",
        // 00.000E00
        "12.340E-03", "12.346E07", "12.300E69", "-31.416E-72",
        // ##0.######E000
        "12.34E-003", "123.4568E006", "12.3E069", "-31.41593E-072",
        // 0.###E0;[0.###E0]
        "1.234E-2", "1.235E8", "1.23E70", "[3.142E-71]"
    };
    double valParse[] =
    {
        0.01234, 123460000, 1.23E70, -3.1416E-71,
        0.01234, 123460000, 1.23E70, -3.1416E-71,
        0.01234, 123456800, 1.23E70, -3.141593E-71,
        0.01234, 123500000, 1.23E70, -3.142E-71,
    };
#else
    // Don't test double conversion
    double* val = 0;
    int32_t val_length = 0;
    char** valFormat = 0;
    double* valParse = 0;
    logln("Warning: Skipping double conversion tests");
#endif

    int32_t lval[] = { 0, -1, 1, 123456789 };
    int32_t lval_length = UPRV_LENGTHOF(lval);
    const char* lvalFormat[] =
    {
        // 0.####E0
        "0E0", "-1E0", "1E0", "1.2346E8",
        // 00.000E00
        "00.000E00", "-10.000E-01", "10.000E-01", "12.346E07",
        // ##0.######E000
        "0E000", "-1E000", "1E000", "123.4568E006",
        // 0.###E0;[0.###E0]
        "0E0", "[1E0]", "1E0", "1.235E8"
    };
    int32_t lvalParse[] =
    {
        0, -1, 1, 123460000,
        0, -1, 1, 123460000,
        0, -1, 1, 123456800,
        0, -1, 1, 123500000,
    };
    int32_t ival = 0, ilval = 0;
    for (int32_t p=0; p<pat_length; ++p)
    {
        DecimalFormat fmt(pat[p], sym, status);
        if (U_FAILURE(status)) { errln("FAIL: Bad status returned by DecimalFormat ct"); continue; }
        UnicodeString pattern;
        logln((UnicodeString)"Pattern \"" + pat[p] + "\" -toPattern-> \"" +
          fmt.toPattern(pattern) + "\"");
        int32_t v;
        for (v=0; v<val_length; ++v)
        {
            UnicodeString s; (*(NumberFormat*)&fmt).format(val[v], s);
            logln((UnicodeString)" " + val[v] + " -format-> " + s);
            if (s != valFormat[v+ival])
                errln((UnicodeString)"FAIL: Expected " + valFormat[v+ival]);

            ParsePosition pos(0);
            Formattable af;
            fmt.parse(s, af, pos);
            double a;
            UBool useEpsilon = FALSE;
            if (af.getType() == Formattable::kLong)
                a = af.getLong();
            else if (af.getType() == Formattable::kDouble) {
                a = af.getDouble();
#if U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400
                // S/390 will show a failure like this:
                //| -3.141592652999999e-271 -format-> -3.1416E-271
                //|                          -parse-> -3.1416e-271
                //| FAIL: Expected -3.141599999999999e-271
                // To compensate, we use an epsilon-based equality
                // test on S/390 only.  We don't want to do this in
                // general because it's less exacting.
                useEpsilon = TRUE;
#endif
            }
            else {
                errln(UnicodeString("FAIL: Non-numeric Formattable returned: ") + pattern + " " + s);
                continue;
            }
            if (pos.getIndex() == s.length())
            {
                logln((UnicodeString)"  -parse-> " + a);
                // Use epsilon comparison as necessary
                if ((useEpsilon &&
                    (uprv_fabs(a - valParse[v+ival]) / a > (2*DBL_EPSILON))) ||
                    (!useEpsilon && a != valParse[v+ival]))
                {
                    errln((UnicodeString)"FAIL: Expected " + valParse[v+ival] + " but got " + a
                        + " on input " + s);
                }
            }
            else {
                errln((UnicodeString)"FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a);
                errln((UnicodeString)"  should be (" + s.length() + " chars) -> " + valParse[v+ival]);
            }
        }
        for (v=0; v<lval_length; ++v)
        {
            UnicodeString s;
            (*(NumberFormat*)&fmt).format(lval[v], s);
            logln((UnicodeString)" " + lval[v] + "L -format-> " + s);
            if (s != lvalFormat[v+ilval])
                errln((UnicodeString)"ERROR: Expected " + lvalFormat[v+ilval] + " Got: " + s);

            ParsePosition pos(0);
            Formattable af;
            fmt.parse(s, af, pos);
            if (af.getType() == Formattable::kLong ||
                af.getType() == Formattable::kInt64) {
                UErrorCode status = U_ZERO_ERROR;
                int32_t a = af.getLong(status);
                if (pos.getIndex() == s.length())
                {
                    logln((UnicodeString)"  -parse-> " + a);
                    if (a != lvalParse[v+ilval])
                        errln((UnicodeString)"FAIL: Expected " + lvalParse[v+ilval] + " but got " + a);
                }
                else
                    errln((UnicodeString)"FAIL: Partial parse (" + pos.getIndex() + " chars) -> " + a);
            }
            else
                errln((UnicodeString)"FAIL: Non-long Formattable returned for " + s
                    + " Double: " + af.getDouble()
                    + ", Long: " + af.getLong());
        }
        ival += val_length;
        ilval += lval_length;
    }
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::TestToPatternScientific11648() {
    UErrorCode status = U_ZERO_ERROR;
    Locale en("en");
    DecimalFormatSymbols sym(en, status);
    DecimalFormat fmt("0.00", sym, status);
    if (U_FAILURE(status)) {
        dataerrln("Error creating DecimalFormat - %s", u_errorName(status));
        return;
    }
    fmt.setScientificNotation(TRUE);
    UnicodeString pattern;
    assertEquals("", "0.00E0", fmt.toPattern(pattern));
    DecimalFormat fmt2(pattern, sym, status);
    assertSuccess("", status);
}

Project: icu
File: numfmtst.cpp

void NumberFormatTest::TestHostClone()
{
    /*
    Verify that a cloned formatter gives the same results
    and is useable after the original has been deleted.
    */
    // This is mainly important on Windows.
    UErrorCode status = U_ZERO_ERROR;
    Locale loc("en_US@compat=host");
    UDate now = Calendar::getNow();
    NumberFormat *full = NumberFormat::createInstance(loc, status);
    if (full == NULL || U_FAILURE(status)) {
        dataerrln("FAIL: Can't create NumberFormat date instance - %s", u_errorName(status));
        return;
    }
    UnicodeString result1;
    full->format(now, result1, status);
    Format *fullClone = full->clone();
    delete full;
    full = NULL;

    UnicodeString result2;
    fullClone->format(now, result2, status);
    if (U_FAILURE(status)) {
        errln("FAIL: format failure.");
    }
    if (result1 != result2) {
        errln("FAIL: Clone returned different result from non-clone.");
    }
    delete fullClone;
}