aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt
diff options
context:
space:
mode:
authorNeil Conway <neilc@samurai.com>2005-05-26 02:10:03 +0000
committerNeil Conway <neilc@samurai.com>2005-05-26 02:10:03 +0000
commit92525dd6c944a4c1233f2eaa6eed8e8d2b89669e (patch)
treeb2d9105c530ac76cabc9ba08adc39de30e0ae69a /src/backend/utils/adt
parent9a1a9865803b5fec8df9dcf74a8ad97c7db5384d (diff)
downloadpostgresql-92525dd6c944a4c1233f2eaa6eed8e8d2b89669e.tar.gz
postgresql-92525dd6c944a4c1233f2eaa6eed8e8d2b89669e.zip
Adjust datetime parsing to be more robust. We now pass the length of the
working buffer into ParseDateTime() and reject too-long input there, rather than checking the length of the input string before calling ParseDateTime(). The old method was bogus because ParseDateTime() can use a variable amount of working space, depending on the content of the input string (e.g. how many fields need to be NUL terminated). This fixes a minor stack overrun -- I don't _think_ it's exploitable, although I won't claim to be an expert. Along the way, fix a bug reported by Mark Dilger: the working buffer allocated by interval_in() was too short, which resulted in rejecting some perfectly valid interval input values. I added a regression test for this fix.
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r--src/backend/utils/adt/date.c26
-rw-r--r--src/backend/utils/adt/datetime.c77
-rw-r--r--src/backend/utils/adt/nabstime.c18
-rw-r--r--src/backend/utils/adt/timestamp.c26
4 files changed, 74 insertions, 73 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 4e344eb091e..0e2eb954656 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.104.4.1 2005/04/23 22:53:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.104.4.2 2005/05/26 02:10:02 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -65,12 +65,10 @@ date_in(PG_FUNCTION_ARGS)
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + 1];
+ char workbuf[MAXDATELEN + 1];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
if (dterr != 0)
@@ -894,15 +892,13 @@ time_in(PG_FUNCTION_ARGS)
int tz;
int nf;
int dterr;
- char lowstr[MAXDATELEN + 1];
+ char workbuf[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
@@ -1734,15 +1730,13 @@ timetz_in(PG_FUNCTION_ARGS)
int tz;
int nf;
int dterr;
- char lowstr[MAXDATELEN + 1];
+ char workbuf[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index ae85fcaf302..996583061a4 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.137.4.1 2005/04/20 17:14:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.137.4.2 2005/05/26 02:10:02 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -700,21 +700,23 @@ TrimTrailingZeros(char *str)
}
}
-
/* ParseDateTime()
* Break string into tokens based on a date/time context.
* Returns 0 if successful, DTERR code if bogus input detected.
*
* timestr - the input string
- * lowstr - workspace for field string storage (must be large enough for
- * a copy of the input string, including trailing null)
+ * workbuf - workspace for field string storage. This must be
+ * larger than the largest legal input for this datetime type --
+ * some additional space will be needed to NUL terminate fields.
+ * buflen - the size of workbuf
* field[] - pointers to field strings are returned in this array
* ftype[] - field type indicators are returned in this array
* maxfields - dimensions of the above two arrays
* *numfields - set to the actual number of fields detected
*
- * The fields extracted from the input are stored as separate, null-terminated
- * strings in the workspace at lowstr. Any text is converted to lower case.
+ * The fields extracted from the input are stored as separate,
+ * null-terminated strings in the workspace at workbuf. Any text is
+ * converted to lower case.
*
* Several field types are assigned:
* DTK_NUMBER - digits and (possibly) a decimal point
@@ -730,12 +732,27 @@ TrimTrailingZeros(char *str)
* DTK_DATE can hold Posix time zones (GMT-8)
*/
int
-ParseDateTime(const char *timestr, char *lowstr,
+ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
char **field, int *ftype, int maxfields, int *numfields)
{
int nf = 0;
const char *cp = timestr;
- char *lp = lowstr;
+ char *bufp = workbuf;
+ const char *bufend = workbuf + buflen;
+
+ /*
+ * Set the character pointed-to by "bufptr" to "newchar", and
+ * increment "bufptr". "end" gives the end of the buffer -- we
+ * return an error if there is no space left to append a character
+ * to the buffer. Note that "bufptr" is evaluated twice.
+ */
+#define APPEND_CHAR(bufptr, end, newchar) \
+ do \
+ { \
+ if (((bufptr) + 1) >= (end)) \
+ return DTERR_BAD_FORMAT; \
+ *(bufptr)++ = newchar; \
+ } while (0)
/* outer loop through fields */
while (*cp != '\0')
@@ -750,23 +767,23 @@ ParseDateTime(const char *timestr, char *lowstr,
/* Record start of current field */
if (nf >= maxfields)
return DTERR_BAD_FORMAT;
- field[nf] = lp;
+ field[nf] = bufp;
/* leading digit? then date or time */
if (isdigit((unsigned char) *cp))
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* time field? */
if (*cp == ':')
{
ftype[nf] = DTK_TIME;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.'))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
/* date field? allow embedded text month */
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
@@ -774,13 +791,13 @@ ParseDateTime(const char *timestr, char *lowstr,
/* save delimiting character to use later */
char delim = *cp;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* second field is all digits? then no embedded text month */
if (isdigit((unsigned char) *cp))
{
ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/*
* insist that the delimiters match to get a
@@ -789,16 +806,16 @@ ParseDateTime(const char *timestr, char *lowstr,
if (*cp == delim)
{
ftype[nf] = DTK_DATE;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) || (*cp == delim))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
}
else
{
ftype[nf] = DTK_DATE;
while (isalnum((unsigned char) *cp) || (*cp == delim))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
}
}
@@ -812,9 +829,9 @@ ParseDateTime(const char *timestr, char *lowstr,
/* Leading decimal point? Then fractional seconds... */
else if (*cp == '.')
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
ftype[nf] = DTK_NUMBER;
}
@@ -826,9 +843,9 @@ ParseDateTime(const char *timestr, char *lowstr,
else if (isalpha((unsigned char) *cp))
{
ftype[nf] = DTK_STRING;
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
while (isalpha((unsigned char) *cp))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
/*
* Full date string with leading text month? Could also be a
@@ -839,15 +856,15 @@ ParseDateTime(const char *timestr, char *lowstr,
char delim = *cp;
ftype[nf] = DTK_DATE;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) || (*cp == delim))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
}
/* sign? then special or numeric timezone */
else if ((*cp == '+') || (*cp == '-'))
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* soak up leading whitespace */
while (isspace((unsigned char) *cp))
cp++;
@@ -855,18 +872,18 @@ ParseDateTime(const char *timestr, char *lowstr,
if (isdigit((unsigned char) *cp))
{
ftype[nf] = DTK_TZ;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.'))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
/* special? */
else if (isalpha((unsigned char) *cp))
{
ftype[nf] = DTK_SPECIAL;
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
while (isalpha((unsigned char) *cp))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
}
/* otherwise something wrong... */
else
@@ -883,7 +900,7 @@ ParseDateTime(const char *timestr, char *lowstr,
return DTERR_BAD_FORMAT;
/* force in a delimiter after each field */
- *lp++ = '\0';
+ *bufp++ = '\0';
nf++;
}
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index 4e2069f5fb6..dd2f6fe95f6 100644
--- a/src/backend/utils/adt/nabstime.c
+++ b/src/backend/utils/adt/nabstime.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.127 2004/12/31 22:01:22 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.127.4.1 2005/05/26 02:10:02 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -306,15 +306,13 @@ abstimein(PG_FUNCTION_ARGS)
*tm = &date;
int dterr;
char *field[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + 1];
+ char workbuf[MAXDATELEN + 1];
int dtype;
int nf,
ftype[MAXDATEFIELDS];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
@@ -711,12 +709,10 @@ reltimein(PG_FUNCTION_ARGS)
char *field[MAXDATEFIELDS];
int nf,
ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + 1];
+ char workbuf[MAXDATELEN + 1];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
if (dterr != 0)
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index fe9cfbcf946..6ade3fb71fd 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.117.4.1 2005/04/01 14:25:39 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.117.4.2 2005/05/26 02:10:02 neilc Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,12 +77,10 @@ timestamp_in(PG_FUNCTION_ARGS)
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + MAXDATEFIELDS];
+ char workbuf[MAXDATELEN + MAXDATEFIELDS];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
@@ -318,12 +316,10 @@ timestamptz_in(PG_FUNCTION_ARGS)
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + MAXDATEFIELDS];
+ char workbuf[MAXDATELEN + MAXDATEFIELDS];
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
+ field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
@@ -494,7 +490,7 @@ interval_in(PG_FUNCTION_ARGS)
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + MAXDATEFIELDS];
+ char workbuf[256];
tm->tm_year = 0;
tm->tm_mon = 0;
@@ -504,10 +500,8 @@ interval_in(PG_FUNCTION_ARGS)
tm->tm_sec = 0;
fsec = 0;
- if (strlen(str) >= sizeof(lowstr))
- dterr = DTERR_BAD_FORMAT;
- else
- dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
+ dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
+ ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
if (dterr != 0)