Skip to content

Commit

Permalink
ICU-22513 Return error if days is too large in IslamicUmalquraCalendar
Browse files Browse the repository at this point in the history
If the year is too large it may overflow the int32_t variable and cause
slow or infinity loop, return error if the year is too large that the
conversion to day may overflow int32_t. Limit the value to max value of
int32_t divide by 400.
  • Loading branch information
FrankYFTang committed Oct 27, 2023
1 parent 3d1dee6 commit cdab88f
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 11 deletions.
20 changes: 16 additions & 4 deletions icu4c/source/i18n/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3078,7 +3078,10 @@ void Calendar::computeTime(UErrorCode& status) {
}

// Compute the Julian day
int32_t julianDay = computeJulianDay();
int32_t julianDay = computeJulianDay(status);
if (U_FAILURE(status)) {
return;
}

double millis = Grego::julianDayToMillis(julianDay);

Expand Down Expand Up @@ -3309,7 +3312,7 @@ int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCod
return rawOffset + dstOffset;
}

int32_t Calendar::computeJulianDay()
int32_t Calendar::computeJulianDay(UErrorCode &status)
{
// We want to see if any of the date fields is newer than the
// JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
Expand All @@ -3333,12 +3336,15 @@ int32_t Calendar::computeJulianDay()
bestField = UCAL_DAY_OF_MONTH;
}

return handleComputeJulianDay(bestField);
return handleComputeJulianDay(bestField, status);
}

// -------------------------------------------

int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode &status) {
if (U_FAILURE(status)) {
return 0;
}
UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
bestField == UCAL_WEEK_OF_MONTH ||
bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
Expand All @@ -3351,6 +3357,12 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
}

internalSet(UCAL_EXTENDED_YEAR, year);
// Return U_ILLEGAL_ARGUMENT_ERROR if year is too large that may cuase int32_t overflow
// later.
if (year > INT32_MAX / 400) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}

#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
Expand Down
14 changes: 10 additions & 4 deletions icu4c/source/i18n/gregocal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,17 +472,20 @@ GregorianCalendar::isLeapYear(int32_t year) const

// -------------------------------------

int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField)
int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode& status)
{
fInvertGregorian = false;

int32_t jd = Calendar::handleComputeJulianDay(bestField);
int32_t jd = Calendar::handleComputeJulianDay(bestField, status);
if (U_FAILURE(status)) {
return 0;
}

if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian*
(internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) &&
jd >= fCutoverJulianDay) {
fInvertGregorian = true; // So that the Julian Jan 1 will be used in handleComputeMonthStart
return Calendar::handleComputeJulianDay(bestField);
return Calendar::handleComputeJulianDay(bestField, status);
}


Expand All @@ -495,7 +498,10 @@ int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField)
__FILE__, __LINE__, jd);
#endif
fInvertGregorian = true;
jd = Calendar::handleComputeJulianDay(bestField);
jd = Calendar::handleComputeJulianDay(bestField, status);
if (U_FAILURE(status)) {
return 0;
}
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ",
__FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
Expand Down
6 changes: 4 additions & 2 deletions icu4c/source/i18n/unicode/calendar.h
Original file line number Diff line number Diff line change
Expand Up @@ -1704,10 +1704,11 @@ class U_I18N_API Calendar : public UObject {
* handleGetMonthLength() to obtain the calendar-specific month
* length.
* @param bestField which field to use to calculate the date
* @param status ICU Error Code
* @return julian day specified by calendar fields.
* @internal
*/
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField);
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode &status);

/**
* Subclasses must override this to convert from week fields
Expand All @@ -1731,10 +1732,11 @@ class U_I18N_API Calendar : public UObject {
/**
* Compute the Julian day from fields. Will determine whether to use
* the JULIAN_DAY field directly, or other fields.
* @param status ICU Error Code
* @return the julian day
* @internal
*/
int32_t computeJulianDay();
int32_t computeJulianDay(UErrorCode &status);

/**
* Compute the milliseconds in the day from the fields. This is a
Expand Down
3 changes: 2 additions & 1 deletion icu4c/source/i18n/unicode/gregocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,10 +495,11 @@ class U_I18N_API GregorianCalendar: public Calendar {
* handleGetMonthLength() to obtain the calendar-specific month
* length.
* @param bestField which field to use to calculate the date
* @param status Fill-in parameter which receives the status of this operation.
* @return julian day specified by calendar fields.
* @internal
*/
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField) override;
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode& status) override;

/**
* Return the number of days in the given month of the given extended
Expand Down
13 changes: 13 additions & 0 deletions icu4c/source/test/intltest/incaltst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ void IntlCalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(TestConsistencyIslamicUmalqura);
TESTCASE_AUTO(TestConsistencyPersian);
TESTCASE_AUTO(TestConsistencyJapanese);
TESTCASE_AUTO(TestIslamicUmalquraCalendarSlow);
TESTCASE_AUTO_END;
}

Expand Down Expand Up @@ -1122,6 +1123,18 @@ void IntlCalendarTest::checkConsistency(const char* locale) {
}
}

void IntlCalendarTest::TestIslamicUmalquraCalendarSlow() {
IcuTestErrorCode status(*this, "TestIslamicUmalquraCalendarSlow");
Locale l("th@calendar=islamic-umalqura");
std::unique_ptr<Calendar> cal(
Calendar::createInstance(l, status));
cal->add(UCAL_YEAR, 1229080905, status);
cal->roll(UCAL_WEEK_OF_MONTH, 1499050699, status);
cal->fieldDifference(0.000000, UCAL_YEAR_WOY, status);
// Ignore the error
status.reset();
}

void IntlCalendarTest::simpleTest(const Locale& loc, const UnicodeString& expect, UDate expectDate, UErrorCode& status)
{
UnicodeString tmp;
Expand Down
1 change: 1 addition & 0 deletions icu4c/source/test/intltest/incaltst.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class IntlCalendarTest: public CalendarTimeZoneTest {
void TestConsistencyIslamicUmalqura();
void TestConsistencyPersian();
void TestConsistencyJapanese();
void TestIslamicUmalquraCalendarSlow();

protected:
// Test a Gregorian-Like calendar
Expand Down
4 changes: 4 additions & 0 deletions icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -6233,6 +6233,10 @@ protected int handleComputeJulianDay(int bestField) {

internalSet(EXTENDED_YEAR, year);

if (year > Long.MAX_VALUE / 400) {
throw new IllegalArgumentException("year is too large");
}

int month = useMonth ? internalGetMonth(getDefaultMonthInYear(year)) : 0;

// Get the Julian day of the day BEFORE the start of this year.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2710,5 +2710,17 @@ public void TestRespectUExtensionFw() { // ICU-22226
Calendar.getInstance(Locale.forLanguageTag(localeIds[i])).getFirstDayOfWeek());
}
}

@Test
public void TestIslamicUmalquraCalendarSlow() { // ICU-22513
Locale loc = new Locale("th@calendar=islamic-umalqura");
Calendar cal = Calendar.getInstance(loc);
cal.clear();
cal.add(Calendar.YEAR, 1229080905);
cal.roll(Calendar.WEEK_OF_MONTH, 1499050699);
cal.fieldDifference(new Date(0), Calendar.YEAR_WOY);

}

}
//eof

0 comments on commit cdab88f

Please sign in to comment.