mirror of
https://gitlab.gnome.org/GNOME/libxml2
synced 2025-03-28 21:33:13 +00:00
Copy xs:duration parser from libexslt
The duration parser in libexslt checks for integer overflows.
This commit is contained in:
parent
18425d3ad5
commit
c0440868c3
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user