[libxslt] Check for overflow in exsltDateParseDuration
- From: Nick Wellnhofer <nwellnhof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libxslt] Check for overflow in exsltDateParseDuration
- Date: Thu, 18 May 2017 16:25:31 +0000 (UTC)
commit 36768481860e4ff46f87d873b1b75007635e4a0b
Author: Nick Wellnhofer <wellnhofer aevum de>
Date: Thu May 18 15:54:54 2017 +0200
Check for overflow in exsltDateParseDuration
Also fix parsing of duSecondFrag, see
https://www.w3.org/TR/xmlschema11-2/#nt-duSeFrag
Fix memory leak in error case.
libexslt/date.c | 190 ++++++++++++++++++++--------------------
tests/exslt/date/seconds.1.out | 4 +
tests/exslt/date/seconds.1.xml | 2 +
tests/exslt/date/seconds.2.out | 2 -
tests/exslt/date/seconds.2.xml | 1 -
5 files changed, 102 insertions(+), 97 deletions(-)
---
diff --git a/libexslt/date.c b/libexslt/date.c
index 31a6e35..d463876 100644
--- a/libexslt/date.c
+++ b/libexslt/date.c
@@ -177,9 +177,12 @@ static const unsigned long 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)
#define DAYS_PER_EPOCH (400 * 365 + 100 - 4 + 1)
#define YEARS_PER_EPOCH 400
@@ -702,56 +705,6 @@ exsltDateFreeDate (exsltDateValPtr date) {
xmlFree(date);
}
-/**
- * PARSE_DIGITS:
- * @num: the integer to fill in
- * @cur: an #xmlChar *
- * @num_type: an integer flag
- *
- * Parses a digits integer and updates @num with the value. @cur is
- * updated to point just after the integer.
- * In case of error, @num_type is set to -1, values of @num and
- * @cur are undefined.
- */
-#define PARSE_DIGITS(num, cur, num_type) \
- if ((*cur < '0') || (*cur > '9')) \
- num_type = -1; \
- else \
- while ((*cur >= '0') && (*cur <= '9')) { \
- num = num * 10 + (*cur - '0'); \
- cur++; \
- }
-
-/**
- * PARSE_NUM:
- * @num: the double to fill in
- * @cur: an #xmlChar *
- * @num_type: an integer flag
- *
- * Parses a float or integer and updates @num with the value. @cur is
- * updated to point just after the number. If the number is a float,
- * then it must have an integer part and a decimal part; @num_type will
- * be set to 1. If there is no decimal part, @num_type is set to zero.
- * In case of error, @num_type is set to -1, values of @num and
- * @cur are undefined.
- */
-#define PARSE_NUM(num, cur, num_type) \
- num = 0; \
- PARSE_DIGITS(num, cur, num_type); \
- if (!num_type && (*cur == '.')) { \
- double mult = 1; \
- cur++; \
- if ((*cur < '0') || (*cur > '9')) \
- num_type = -1; \
- else \
- num_type = 1; \
- while ((*cur >= '0') && (*cur <= '9')) { \
- mult /= 10; \
- num += (*cur - '0') * mult; \
- cur++; \
- } \
- }
-
#ifdef WITH_TIME
/**
* exsltDateCurrent:
@@ -1032,6 +985,8 @@ exsltDateParseDuration (const xmlChar *duration)
exsltDateValPtr dur;
int isneg = 0;
unsigned int seq = 0;
+ long days, secs = 0;
+ double sec_frac = 0.0;
if (duration == NULL)
return NULL;
@@ -1050,10 +1005,10 @@ exsltDateParseDuration (const xmlChar *duration)
return NULL;
while (*cur != 0) {
- double num;
- int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
+ long num = 0;
+ size_t has_digits = 0;
+ int has_frac = 0;
const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
- const double multi[] = { 0.0, 0.0, 0.0, 3600.0, 60.0, 1.0, 0.0};
/* input string should be empty or invalid date/time item */
if (seq >= sizeof(desig))
@@ -1061,56 +1016,103 @@ exsltDateParseDuration (const xmlChar *duration)
/* T designator must be present for time items */
if (*cur == 'T') {
- if (seq <= 3) {
- seq = 3;
- cur++;
- } else
- return NULL;
+ 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);
-
- if ((num_type == -1) || (*cur == 0))
- goto error;
-
- /* update duration based on item type */
- while (seq < sizeof(desig)) {
- if (*cur == desig[seq]) {
+ /* Parse integral part. */
+ while (*cur >= '0' && *cur <= '9') {
+ long digit = *cur - '0';
- /* verify numeric type; only seconds can be float */
- if ((num_type != 0) && (seq < (sizeof(desig)-1)))
- goto error;
+ if (num > LONG_MAX / 10)
+ goto error;
+ num *= 10;
+ if (num > LONG_MAX - digit)
+ goto error;
+ num += digit;
- switch (seq) {
- case 0:
- dur->value.dur.mon = (long)num * 12;
- break;
- case 1:
- dur->value.dur.mon += (long)num;
- break;
- case 2:
- dur->value.dur.day = (long)num;
- default:
- /* convert to seconds using multiplier */
- dur->value.dur.sec += num * multi[seq];
- seq++;
- break;
- }
+ has_digits = 1;
+ cur++;
+ }
- 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)
+ }
+
+ while (*cur != desig[seq]) {
+ seq++;
+ /* No T designator or invalid char. */
+ if (seq == 3 || seq == sizeof(desig))
goto error;
}
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++;
}
- /* Clamp seconds to 0..SECS_PER_DAY range. */
- dur->value.dur.day += (long)(dur->value.dur.sec / SECS_PER_DAY);
- dur->value.dur.sec = fmod(dur->value.dur.sec, SECS_PER_DAY);
+ 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;
diff --git a/tests/exslt/date/seconds.1.out b/tests/exslt/date/seconds.1.out
index 5d5e5da..9044a31 100644
--- a/tests/exslt/date/seconds.1.out
+++ b/tests/exslt/date/seconds.1.out
@@ -17,6 +17,10 @@ seconds : -P0Y0M31DT10H10M10.09S
result : -2715010.09
seconds : PT100H100M100.001S
result : 366100.001
+seconds : PT10H10M.5S
+result : 36600.5
+seconds : PT10H10M5.S
+result : 36605
seconds : 2001
result : 978307200
seconds : 2001-10-29T10:31:07
diff --git a/tests/exslt/date/seconds.1.xml b/tests/exslt/date/seconds.1.xml
index 24d58fb..6e33f5c 100644
--- a/tests/exslt/date/seconds.1.xml
+++ b/tests/exslt/date/seconds.1.xml
@@ -10,6 +10,8 @@
<date duration="P0Y0M31DT10H10M10.09S"/>
<date duration="-P0Y0M31DT10H10M10.09S"/>
<date duration="PT100H100M100.001S"/>
+ <date duration="PT10H10M.5S"/>
+ <date duration="PT10H10M5.S"/>
<!-- date/times -->
<date duration="2001"/>
<date duration="2001-10-29T10:31:07"/>
diff --git a/tests/exslt/date/seconds.2.out b/tests/exslt/date/seconds.2.out
index e804d94..944b714 100644
--- a/tests/exslt/date/seconds.2.out
+++ b/tests/exslt/date/seconds.2.out
@@ -7,8 +7,6 @@ seconds : PT-10D
result : NaN
seconds : --PT10H
result : NaN
-seconds : PT10H10M.5S
-result : NaN
seconds : PYT0.00001S
result : NaN
seconds : PT49.00001
diff --git a/tests/exslt/date/seconds.2.xml b/tests/exslt/date/seconds.2.xml
index 276baef..9052b8f 100644
--- a/tests/exslt/date/seconds.2.xml
+++ b/tests/exslt/date/seconds.2.xml
@@ -5,7 +5,6 @@
<date duration="P-T10D"/>
<date duration="PT-10D"/>
<date duration="--PT10H"/>
- <date duration="PT10H10M.5S"/>
<date duration="PYT0.00001S"/>
<date duration="PT49.00001"/>
<date duration="P0Y0MDT10H10M10.09S"/>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]