Copy xs:duration parser from libexslt

The duration parser in libexslt checks for integer overflows.
This commit is contained in:
Nick Wellnhofer 2020-06-22 13:08:11 +02:00
parent 18425d3ad5
commit c0440868c3

View File

@ -1160,9 +1160,12 @@ static const unsigned int daysInMonthLeap[12] =
#define VALID_DATETIME(dt) \
(VALID_DATE(dt) && VALID_TIME(dt))
#define SECS_PER_MIN (60)
#define SECS_PER_HOUR (60 * SECS_PER_MIN)
#define SECS_PER_DAY (24 * SECS_PER_HOUR)
#define SECS_PER_MIN 60
#define MINS_PER_HOUR 60
#define HOURS_PER_DAY 24
#define SECS_PER_HOUR (MINS_PER_HOUR * SECS_PER_MIN)
#define SECS_PER_DAY (HOURS_PER_DAY * SECS_PER_HOUR)
#define MINS_PER_DAY (HOURS_PER_DAY * MINS_PER_HOUR)
static const long dayInYearByMonth[12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
@ -1798,10 +1801,8 @@ xmlSchemaValidateDuration (xmlSchemaTypePtr type ATTRIBUTE_UNUSED,
xmlSchemaValPtr dur;
int isneg = 0;
unsigned int seq = 0;
double num;
int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
long days, secs = 0;
double sec_frac = 0.0;
if (duration == NULL)
return -1;
@ -1826,6 +1827,10 @@ xmlSchemaValidateDuration (xmlSchemaTypePtr type ATTRIBUTE_UNUSED,
return -1;
while (*cur != 0) {
long num = 0;
size_t has_digits = 0;
int has_frac = 0;
const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
/* input string should be empty or invalid date/time item */
if (seq >= sizeof(desig))
@ -1833,53 +1838,104 @@ xmlSchemaValidateDuration (xmlSchemaTypePtr type ATTRIBUTE_UNUSED,
/* T designator must be present for time items */
if (*cur == 'T') {
if (seq <= 3) {
seq = 3;
cur++;
} else
return 1;
if (seq > 3)
goto error;
cur++;
seq = 3;
} else if (seq == 3)
goto error;
/* parse the number portion of the item */
PARSE_NUM(num, cur, num_type);
/* Parse integral part. */
while (*cur >= '0' && *cur <= '9') {
long digit = *cur - '0';
if ((num_type == -1) || (*cur == 0))
goto error;
if (num > LONG_MAX / 10)
goto error;
num *= 10;
if (num > LONG_MAX - digit)
goto error;
num += digit;
/* update duration based on item type */
while (seq < sizeof(desig)) {
if (*cur == desig[seq]) {
has_digits = 1;
cur++;
}
/* verify numeric type; only seconds can be float */
if ((num_type != 0) && (seq < (sizeof(desig)-1)))
goto error;
switch (seq) {
case 0:
dur->value.dur.mon = (long)num * 12;
break;
case 1:
dur->value.dur.mon += (long)num;
break;
default:
/* convert to seconds using multiplier */
dur->value.dur.sec += num * multi[seq];
seq++;
break;
}
break; /* exit loop */
if (*cur == '.') {
/* Parse fractional part. */
double mult = 1.0;
cur++;
has_frac = 1;
while (*cur >= '0' && *cur <= '9') {
mult /= 10.0;
sec_frac += (*cur - '0') * mult;
has_digits = 1;
cur++;
}
/* no date designators found? */
if ((++seq == 3) || (seq == 6))
}
while (*cur != desig[seq]) {
seq++;
/* No T designator or invalid char. */
if (seq == 3 || seq == sizeof(desig))
goto error;
}
cur++;
if (collapse)
while IS_WSP_BLANK_CH(*cur) cur++;
if (!has_digits || (has_frac && (seq != 5)))
goto error;
switch (seq) {
case 0:
/* Year */
if (num > LONG_MAX / 12)
goto error;
dur->value.dur.mon = num * 12;
break;
case 1:
/* Month */
if (dur->value.dur.mon > LONG_MAX - num)
goto error;
dur->value.dur.mon += num;
break;
case 2:
/* Day */
dur->value.dur.day = num;
break;
case 3:
/* Hour */
days = num / HOURS_PER_DAY;
if (dur->value.dur.day > LONG_MAX - days)
goto error;
dur->value.dur.day += days;
secs = (num % HOURS_PER_DAY) * SECS_PER_HOUR;
break;
case 4:
/* Minute */
days = num / MINS_PER_DAY;
if (dur->value.dur.day > LONG_MAX - days)
goto error;
dur->value.dur.day += days;
secs += (num % MINS_PER_DAY) * SECS_PER_MIN;
break;
case 5:
/* Second */
days = num / SECS_PER_DAY;
if (dur->value.dur.day > LONG_MAX - days)
goto error;
dur->value.dur.day += days;
secs += num % SECS_PER_DAY;
break;
}
seq++;
}
days = secs / SECS_PER_DAY;
if (dur->value.dur.day > LONG_MAX - days)
goto error;
dur->value.dur.day += days;
dur->value.dur.sec = (secs % SECS_PER_DAY) + sec_frac;
if (isneg) {
dur->value.dur.mon = -dur->value.dur.mon;
dur->value.dur.day = -dur->value.dur.day;