From 6ac6fdda74948d0a2ddd51ee81a672baa5c2bf35 Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Mon, 13 May 2024 15:58:08 -0700 Subject: [PATCH] ICU-22730 propagate error avoid overflow --- icu4c/source/i18n/basictz.cpp | 14 +++++++--- icu4c/source/i18n/calendar.cpp | 2 +- icu4c/source/i18n/chnsecal.cpp | 2 +- icu4c/source/i18n/erarules.cpp | 3 ++- icu4c/source/i18n/gregoimp.cpp | 13 +++++++--- icu4c/source/i18n/gregoimp.h | 13 ++++++---- icu4c/source/i18n/indiancal.cpp | 9 ++++--- icu4c/source/i18n/olsontz.cpp | 5 +++- icu4c/source/i18n/simpletz.cpp | 6 +++-- icu4c/source/i18n/timezone.cpp | 3 ++- icu4c/source/i18n/tzrule.cpp | 8 ++++-- icu4c/source/i18n/vtzone.cpp | 46 ++++++++++++++++++++++----------- 12 files changed, 83 insertions(+), 41 deletions(-) diff --git a/icu4c/source/i18n/basictz.cpp b/icu4c/source/i18n/basictz.cpp index d03c50adac0f..3674e4de4a4b 100644 --- a/icu4c/source/i18n/basictz.cpp +++ b/icu4c/source/i18n/basictz.cpp @@ -165,7 +165,8 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, // Get local wall time for the next transition time Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, - year, month, dom, dow, doy, mid); + year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) return; int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); // Create DOW rule DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); @@ -192,7 +193,8 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, // Get local wall time for the next transition time Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), - year, month, dom, dow, doy, mid); + year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) return; weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); // Generate another DOW rule dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); @@ -223,7 +225,8 @@ BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, // Generate another DOW rule Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), - year, month, dom, dow, doy, mid); + year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) return; weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); tr.getTo()->getName(name); @@ -484,7 +487,10 @@ BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, } else { // Calculate the transition year int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid); + Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) { + return; + } // Re-create the rule ar->getName(name); LocalPointer newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index 87cba3b048d9..82936b4d16a4 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -1583,7 +1583,7 @@ void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) { } Grego::dayToFields(julianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, - fGregorianDayOfYear); + fGregorianDayOfYear, ec); } /** diff --git a/icu4c/source/i18n/chnsecal.cpp b/icu4c/source/i18n/chnsecal.cpp index 030cc5edd0e3..c3a57ea1f190 100644 --- a/icu4c/source/i18n/chnsecal.cpp +++ b/icu4c/source/i18n/chnsecal.cpp @@ -372,7 +372,7 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U int32_t unusedDayOfWeek; int32_t unusedDayOfMonth; int32_t unusedDayOfYear; - Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear); + Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear, status); struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, newMoon, status); if (U_FAILURE(status)) { diff --git a/icu4c/source/i18n/erarules.cpp b/icu4c/source/i18n/erarules.cpp index 65405bb84aac..ff270ea696be 100644 --- a/icu4c/source/i18n/erarules.cpp +++ b/icu4c/source/i18n/erarules.cpp @@ -306,7 +306,8 @@ void EraRules::initCurrentEra() { } int year, month0, dom, dow, doy, mid; - Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid); + Grego::timeToFields(localMillis, year, month0, dom, dow, doy, mid, ec); + if (U_FAILURE(ec)) return; int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom); int eraIdx = numEras - 1; while (eraIdx > 0) { diff --git a/icu4c/source/i18n/gregoimp.cpp b/icu4c/source/i18n/gregoimp.cpp index 57499bd30857..9ae0c04c6570 100644 --- a/icu4c/source/i18n/gregoimp.cpp +++ b/icu4c/source/i18n/gregoimp.cpp @@ -118,10 +118,14 @@ int64_t Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) { } void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy) { + int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status) { + if (U_FAILURE(status)) return; // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar) - day += JULIAN_1970_CE - JULIAN_1_CE; + if (uprv_add32_overflow(day, JULIAN_1970_CE - JULIAN_1_CE, &day)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } // Convert from the day number to the multiple radix // representation. We use 400-year, 100-year, and 4-year cycles. @@ -156,11 +160,12 @@ void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month, } void Grego::timeToFields(UDate time, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) { + int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status) { + if (U_FAILURE(status)) return; double millisInDay; double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, &millisInDay); mid = (int32_t)millisInDay; - dayToFields(day, year, month, dom, dow, doy); + dayToFields(day, year, month, dom, dow, doy, status); } int32_t Grego::dayOfWeek(int32_t day) { diff --git a/icu4c/source/i18n/gregoimp.h b/icu4c/source/i18n/gregoimp.h index cd7694040d66..bd6664ea752b 100644 --- a/icu4c/source/i18n/gregoimp.h +++ b/icu4c/source/i18n/gregoimp.h @@ -208,9 +208,10 @@ class Grego { * @param dom output parameter to receive day-of-month (1-based) * @param dow output parameter to receive day-of-week (1-based, 1==Sun) * @param doy output parameter to receive day-of-year (1-based) + * @param status error code. */ static void dayToFields(int32_t day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy); + int32_t& dom, int32_t& dow, int32_t& doy, UErrorCode& status); /** * Convert a 1970-epoch day number to proleptic Gregorian year, @@ -220,9 +221,10 @@ class Grego { * @param month output parameter to receive month (0-based, 0==Jan) * @param dom output parameter to receive day-of-month (1-based) * @param dow output parameter to receive day-of-week (1-based, 1==Sun) + * @param status error code. */ static inline void dayToFields(int32_t day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow); + int32_t& dom, int32_t& dow, UErrorCode& status); /** * Convert a 1970-epoch milliseconds to proleptic Gregorian year, @@ -234,9 +236,10 @@ class Grego { * @param dow output parameter to receive day-of-week (1-based, 1==Sun) * @param doy output parameter to receive day-of-year (1-based) * @param mid output parameter to receive millis-in-day + * @param status error code. */ static void timeToFields(UDate time, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid); + int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid, UErrorCode& status); /** * Return the day of week on the 1970-epoch day @@ -303,9 +306,9 @@ Grego::previousMonthLength(int y, int m) { } inline void Grego::dayToFields(int32_t day, int32_t& year, int32_t& month, - int32_t& dom, int32_t& dow) { + int32_t& dom, int32_t& dow, UErrorCode& status) { int32_t doy_unused; - dayToFields(day,year,month,dom,dow,doy_unused); + dayToFields(day,year,month,dom,dow,doy_unused, status); } inline double Grego::julianDayToMillis(int32_t julian) diff --git a/icu4c/source/i18n/indiancal.cpp b/icu4c/source/i18n/indiancal.cpp index 1cca8e5f8412..a15f59db52dc 100644 --- a/icu4c/source/i18n/indiancal.cpp +++ b/icu4c/source/i18n/indiancal.cpp @@ -148,10 +148,10 @@ static double gregorianToJD(int32_t year, int32_t month, int32_t date) { * Month is 0 based. * @param jd The Julian Day */ -static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3]) { +static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3], UErrorCode& status) { int32_t gdow; Grego::dayToFields(jd - kEpochStartAsJulianDay, - gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow); + gregorianDate[0], gregorianDate[1], gregorianDate[2], gdow, status); return gregorianDate; } @@ -263,13 +263,14 @@ int32_t IndianCalendar::handleGetExtendedYear(UErrorCode& status) { * method is called. The getGregorianXxx() methods return Gregorian * calendar equivalents for the given Julian day. */ -void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) { +void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { double jdAtStartOfGregYear; int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday; int32_t gregorianYear; // Stores gregorian date corresponding to Julian day; int32_t gd[3]; - gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day + gregorianYear = jdToGregorian(julianDay, gd, status)[0]; // Gregorian date for Julian day + if (U_FAILURE(status)) return; IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0) diff --git a/icu4c/source/i18n/olsontz.cpp b/icu4c/source/i18n/olsontz.cpp index 924502f5371d..cac3612c540b 100644 --- a/icu4c/source/i18n/olsontz.cpp +++ b/icu4c/source/i18n/olsontz.cpp @@ -569,7 +569,10 @@ UBool OlsonTimeZone::useDaylightTime() const { } int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(current, year, month, dom, dow, doy, mid); + UErrorCode status = U_ZERO_ERROR; + Grego::timeToFields(current, year, month, dom, dow, doy, mid, status); + U_ASSERT(U_SUCCESS(status)); + if (U_FAILURE(status)) return false; // If error, just return false. // Find start of this year, and start of next year double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY; diff --git a/icu4c/source/i18n/simpletz.cpp b/icu4c/source/i18n/simpletz.cpp index f144577155cd..31ee69e7d21c 100644 --- a/icu4c/source/i18n/simpletz.cpp +++ b/icu4c/source/i18n/simpletz.cpp @@ -526,7 +526,8 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT } int32_t day = dday; - Grego::dayToFields(day, year, month, dom, dow); + Grego::dayToFields(day, year, month, dom, dow, status); + if (U_FAILURE(status)) return; savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, (uint8_t) dow, millis, @@ -554,7 +555,8 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT } if (recalc) { day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis); - Grego::dayToFields(day, year, month, dom, dow); + Grego::dayToFields(day, year, month, dom, dow, status); + if (U_FAILURE(status)) return; savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, (uint8_t) dow, millis, Grego::monthLength(year, month), diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp index 2f6794c50601..a9fb316a15ab 100644 --- a/icu4c/source/i18n/timezone.cpp +++ b/icu4c/source/i18n/timezone.cpp @@ -738,7 +738,8 @@ void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, ec = U_ILLEGAL_ARGUMENT_ERROR; return; } - Grego::dayToFields(day, year, month, dom, dow); + Grego::dayToFields(day, year, month, dom, dow, ec); + if (U_FAILURE(ec)) return; dstOffset = getOffset(GregorianCalendar::AD, year, month, dom, (uint8_t) dow, millis, diff --git a/icu4c/source/i18n/tzrule.cpp b/icu4c/source/i18n/tzrule.cpp index a3522f1d8b2d..2ec57c9c1ba8 100644 --- a/icu4c/source/i18n/tzrule.cpp +++ b/icu4c/source/i18n/tzrule.cpp @@ -356,7 +356,9 @@ AnnualTimeZoneRule::getNextStart(UDate base, UBool inclusive, UDate& result) const { int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(base, year, month, dom, dow, doy, mid); + UErrorCode status = U_ZERO_ERROR; + Grego::timeToFields(base, year, month, dom, dow, doy, mid, status); + U_ASSERT(U_SUCCESS(status)); if (year < fStartYear) { return getFirstStart(prevRawOffset, prevDSTSavings, result); } @@ -380,7 +382,9 @@ AnnualTimeZoneRule::getPreviousStart(UDate base, UBool inclusive, UDate& result) const { int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(base, year, month, dom, dow, doy, mid); + UErrorCode status = U_ZERO_ERROR; + Grego::timeToFields(base, year, month, dom, dow, doy, mid, status); + U_ASSERT(U_SUCCESS(status)); if (year > fEndYear) { return getFinalStart(prevRawOffset, prevDSTSavings, result); } diff --git a/icu4c/source/i18n/vtzone.cpp b/icu4c/source/i18n/vtzone.cpp index 31ad09c721f3..250cc70193e1 100644 --- a/icu4c/source/i18n/vtzone.cpp +++ b/icu4c/source/i18n/vtzone.cpp @@ -180,9 +180,11 @@ static UnicodeString& appendMillis(UDate date, UnicodeString& str) { /* * Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME */ -static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) { +static UnicodeString& getDateTimeString(UDate time, UnicodeString& str, UErrorCode& status) { + if (U_FAILURE(status)) {return str;} int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(time, year, month, dom, dow, doy, mid); + Grego::timeToFields(time, year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) {return str;} str.remove(); appendAsciiDigits(year, 4, str); @@ -206,8 +208,8 @@ static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) { /* * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME */ -static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str) { - getDateTimeString(time, str); +static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str, UErrorCode& status) { + getDateTimeString(time, str, status); str.append((char16_t)0x005A /*'Z'*/); return str; } @@ -675,7 +677,10 @@ static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOff // Calculate start/end year and missing fields int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID; Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM, - startDOW, startDOY, startMID); + startDOW, startDOY, startMID, status); + if (U_FAILURE(status)) { + return nullptr; + } if (month == -1) { // If BYMONTH is not set, use the month of DTSTART month = startMonth; @@ -688,7 +693,8 @@ static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOff int32_t endYear; if (until != MIN_MILLIS) { int32_t endMonth, endDOM, endDOW, endDOY, endMID; - Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID); + Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID, status); + if (U_FAILURE(status)) return nullptr; } else { endYear = AnnualTimeZoneRule::MAX_YEAR; } @@ -1669,7 +1675,8 @@ VTimeZone::parse(UErrorCode& status) { } else { // Update the end year int32_t y, m, d, dow, doy, mid; - Grego::timeToFields(start, y, m, d, dow, doy, mid); + Grego::timeToFields(start, y, m, d, dow, doy, mid, status); + if (U_FAILURE(status)) return; newRule.adoptInsteadAndCheckErrorCode( new AnnualTimeZoneRule( finalRule->getName(tznam), @@ -1709,6 +1716,7 @@ VTimeZone::parse(UErrorCode& status) { void VTimeZone::write(VTZWriter& writer, UErrorCode& status) const { + if (U_FAILURE(status)) return; if (vtzlines != nullptr) { for (int32_t i = 0; i < vtzlines->size(); i++) { UnicodeString *line = (UnicodeString*)vtzlines->elementAt(i); @@ -1723,7 +1731,8 @@ VTimeZone::write(VTZWriter& writer, UErrorCode& status) const { UnicodeString utcString; writer.write(ICAL_LASTMOD); writer.write(COLON); - writer.write(getUTCDateTimeString(lastmod, utcString)); + writer.write(getUTCDateTimeString(lastmod, utcString, status)); + if (U_FAILURE(status)) return; writer.write(ICAL_NEWLINE); } else { writer.write(*line); @@ -1913,7 +1922,8 @@ VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz, int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings(); int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings(); int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings(); - Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid); + Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid, status); + if (U_FAILURE(status)) return; int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); UBool sameRule = false; const AnnualTimeZoneRule *atzrule; @@ -2158,7 +2168,7 @@ VTimeZone::writeHeaders(VTZWriter& writer, UErrorCode& status) const { UnicodeString lastmodStr; writer.write(ICAL_LASTMOD); writer.write(COLON); - writer.write(getUTCDateTimeString(lastmod, lastmodStr)); + writer.write(getUTCDateTimeString(lastmod, lastmodStr, status)); writer.write(ICAL_NEWLINE); } } @@ -2195,8 +2205,11 @@ VTimeZone::writeZonePropsByTime(VTZWriter& writer, UBool isDst, const UnicodeStr writer.write(ICAL_RDATE); writer.write(COLON); UnicodeString timestr; - writer.write(getDateTimeString(time + fromOffset, timestr)); + writer.write(getDateTimeString(time + fromOffset, timestr, status)); writer.write(ICAL_NEWLINE); + if (U_FAILURE(status)) { + return; + } } endZoneProps(writer, isDst, status); if (U_FAILURE(status)) { @@ -2229,7 +2242,7 @@ VTimeZone::writeZonePropsByDOM(VTZWriter& writer, UBool isDst, const UnicodeStri appendAsciiDigits(dayOfMonth, 0, dstr); writer.write(dstr); if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); + appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr, status), status); if (U_FAILURE(status)) { return; } @@ -2265,7 +2278,7 @@ VTimeZone::writeZonePropsByDOW(VTZWriter& writer, UBool isDst, const UnicodeStri writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU... if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); + appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr, status), status); if (U_FAILURE(status)) { return; } @@ -2390,7 +2403,7 @@ VTimeZone::writeZonePropsByDOW_GEQ_DOM_sub(VTZWriter& writer, int32_t month, int } if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); + appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr, status), status); if (U_FAILURE(status)) { return; } @@ -2529,7 +2542,10 @@ VTimeZone::beginZoneProps(VTZWriter& writer, UBool isDst, const UnicodeString& z // DTSTART writer.write(ICAL_DTSTART); writer.write(COLON); - writer.write(getDateTimeString(startTime + fromOffset, dstr)); + writer.write(getDateTimeString(startTime + fromOffset, dstr, status)); + if (U_FAILURE(status)) { + return; + } writer.write(ICAL_NEWLINE); }