ICU-22513 Return error if days is too large in IslamicUmalquraCalendar

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.
This commit is contained in:
Frank Tang 2023-10-26 13:17:10 -07:00 committed by Frank Yung-Fong Tang
parent 3d1dee6837
commit cdab88ff4e
8 changed files with 62 additions and 11 deletions

View file

@ -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);
@ -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
@ -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);
@ -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);

View file

@ -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);
}
@ -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");

View file

@ -1704,10 +1704,11 @@ protected:
* 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
@ -1731,10 +1732,11 @@ protected:
/**
* 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

View file

@ -495,10 +495,11 @@ public:
* 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

View file

@ -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;
}
@ -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;

View file

@ -60,6 +60,7 @@ public:
void TestConsistencyIslamicUmalqura();
void TestConsistencyPersian();
void TestConsistencyJapanese();
void TestIslamicUmalquraCalendarSlow();
protected:
// Test a Gregorian-Like calendar

View file

@ -6233,6 +6233,10 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
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.

View file

@ -2710,5 +2710,17 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
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