Commit b71310d8 authored by Thomas G. Lockhart's avatar Thomas G. Lockhart

Add guard code to protect from buffer overruns on long date/time input

 strings. Should go back in and look at doing this a bit more elegantly
 and (hopefully) cheaper. Probably not too bad anyway, but it seems a
 shame to scan the strings twice: once for length for this buffer overrun
 protection, and once to parse the line.
Remove use of pow() in date/time handling; was already gone from everything
 *but* the time data types.
Define macros for handling typmod manipulation for date/time types.
 Should be more robust than all of that brute-force inline code.
Rename macros for masking and typmod manipulation to put TIMESTAMP_
 or INTERVAL_ in front of the macro name, to reduce the possibility
 of name space collisions.
parent e025bb7a
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.69 2002/06/20 20:29:36 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.70 2002/08/04 06:44:47 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -57,6 +57,9 @@ date_in(PG_FUNCTION_ARGS)
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad date external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
elog(ERROR, "Bad date external representation '%s'", str);
......@@ -518,6 +521,9 @@ time_in(PG_FUNCTION_ARGS)
int dtype;
int ftype[MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad time external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, NULL) != 0))
elog(ERROR, "Bad time external representation '%s'", str);
......@@ -606,37 +612,85 @@ time_scale(PG_FUNCTION_ARGS)
PG_RETURN_TIMEADT(result);
}
/* AdjustTimeForTypmod()
* Force the precision of the time value to a specified value.
* Uses *exactly* the same code as in AdjustTimestampForTypemod()
* but we make a separate copy because those types do not
* have a fundamental tie together but rather a coincidence of
* implementation. - thomas
*/
static void
AdjustTimeForTypmod(TimeADT *time, int32 typmod)
{
if ((typmod >= 0) && (typmod <= MAX_TIME_PRECISION))
{
#ifdef HAVE_INT64_TIMESTAMP
static int64 TimeScale = INT64CONST(1000000);
static const int64 TimeScales[MAX_TIMESTAMP_PRECISION+1] = {
INT64CONST(1000000),
INT64CONST(100000),
INT64CONST(10000),
INT64CONST(1000),
INT64CONST(100),
INT64CONST(10),
INT64CONST(1)
};
static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION+1] = {
INT64CONST(-500000),
INT64CONST(-50000),
INT64CONST(-5000),
INT64CONST(-500),
INT64CONST(-50),
INT64CONST(-5),
INT64CONST(0)
};
#else
static double TimeScale = 1;
static const double TimeScales[MAX_TIMESTAMP_PRECISION+1] = {
1,
10,
100,
1000,
10000,
100000,
1000000
};
static const double TimeOffsets[MAX_TIMESTAMP_PRECISION+1] = {
0.5,
0.05,
0.005,
0.0005,
0.00005,
0.000005,
0.0000005
};
#endif
static int32 TimeTypmod = 0;
if (typmod != TimeTypmod)
{
if ((typmod >= 0) && (typmod <= MAX_TIME_PRECISION))
{
#ifdef HAVE_INT64_TIMESTAMP
TimeScale = pow(10.0, (MAX_TIME_PRECISION-typmod));
#else
TimeScale = pow(10.0, typmod);
#endif
TimeTypmod = typmod;
/* we have different truncation behavior depending on sign */
if (*time >= INT64CONST(0))
{
*time = ((*time / TimeScales[typmod])
* TimeScales[typmod]);
}
else
{
*time = (((*time + TimeOffsets[typmod]) / TimeScales[typmod])
* TimeScales[typmod]);
}
#ifdef HAVE_INT64_TIMESTAMP
*time = ((*time / TimeScale) * TimeScale);
if (*time >= INT64CONST(86400000000))
*time -= INT64CONST(86400000000);
#else
*time = (rint(((double) *time) * TimeScale) / TimeScale);
if (*time >= 86400)
*time -= 86400;
/* we have different truncation behavior depending on sign */
if (*time >= 0)
{
*time = (rint(((double) *time) * TimeScales[typmod])
/ TimeScales[typmod]);
}
else
{
/* Scale and truncate first, then add to help the rounding behavior */
*time = (rint((((double) *time) * TimeScales[typmod]) + TimeOffsets[typmod])
/ TimeScales[typmod]);
}
#endif
}
......@@ -1269,6 +1323,10 @@ timetz_in(PG_FUNCTION_ARGS)
int dtype;
int ftype[MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad time with time zone"
" external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
elog(ERROR, "Bad time external representation '%s'", str);
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.30 2002/06/20 20:29:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.31 2002/08/04 06:44:47 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,9 +29,6 @@
#include "mb/pg_wchar.h"
#endif
#define MASK(b) (1 << (b))
#define MAX_INT32_LEN 11
#define _textin(str) DirectFunctionCall1(textin, CStringGetDatum(str))
......@@ -258,52 +255,63 @@ format_type_internal(Oid type_oid, int32 typemod,
case INTERVALOID:
if (with_typemod)
{
int fields = typemod >> 16;
int precision = typemod & 0xFFFF;
int fields = INTERVAL_RANGE(typemod);
int precision = INTERVAL_PRECISION(typemod);
const char *fieldstr;
switch (fields)
{
case MASK(YEAR):
case INTERVAL_MASK(YEAR):
fieldstr = " year";
break;
case MASK(MONTH):
case INTERVAL_MASK(MONTH):
fieldstr = " month";
break;
case MASK(DAY):
case INTERVAL_MASK(DAY):
fieldstr = " day";
break;
case MASK(HOUR):
case INTERVAL_MASK(HOUR):
fieldstr = " hour";
break;
case MASK(MINUTE):
case INTERVAL_MASK(MINUTE):
fieldstr = " minute";
break;
case MASK(SECOND):
case INTERVAL_MASK(SECOND):
fieldstr = " second";
break;
case MASK(YEAR) | MASK(MONTH):
case INTERVAL_MASK(YEAR)
| INTERVAL_MASK(MONTH):
fieldstr = " year to month";
break;
case MASK(DAY) | MASK(HOUR):
case INTERVAL_MASK(DAY)
| INTERVAL_MASK(HOUR):
fieldstr = " day to hour";
break;
case MASK(DAY) | MASK(HOUR) | MASK(MINUTE):
case INTERVAL_MASK(DAY)
| INTERVAL_MASK(HOUR)
| INTERVAL_MASK(MINUTE):
fieldstr = " day to minute";
break;
case MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
case INTERVAL_MASK(DAY)
| INTERVAL_MASK(HOUR)
| INTERVAL_MASK(MINUTE)
| INTERVAL_MASK(SECOND):
fieldstr = " day to second";
break;
case MASK(HOUR) | MASK(MINUTE):
case INTERVAL_MASK(HOUR)
| INTERVAL_MASK(MINUTE):
fieldstr = " hour to minute";
break;
case MASK(HOUR) | MASK(MINUTE) | MASK(SECOND):
case INTERVAL_MASK(HOUR)
| INTERVAL_MASK(MINUTE)
| INTERVAL_MASK(SECOND):
fieldstr = " hour to second";
break;
case MASK(MINUTE) | MASK(SECOND):
case INTERVAL_MASK(MINUTE)
| INTERVAL_MASK(SECOND):
fieldstr = " minute to second";
break;
case 0x7FFF:
case INTERVAL_FULL_RANGE:
fieldstr = "";
break;
default:
......@@ -311,7 +319,7 @@ format_type_internal(Oid type_oid, int32 typemod,
fieldstr = "";
break;
}
if (precision != 0xFFFF)
if (precision != INTERVAL_FULL_PRECISION)
buf = psnprintf(100, "interval(%d)%s",
precision, fieldstr);
else
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.95 2002/06/20 20:29:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.96 2002/08/04 06:44:47 thomas Exp $
*
* NOTES
*
......@@ -486,8 +486,8 @@ nabstimein(PG_FUNCTION_ARGS)
int nf,
ftype[MAXDATEFIELDS];
if (strlen(str) > MAXDATELEN)
elog(ERROR, "Bad (length) abstime external representation '%s'", str);
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad abstime external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
......@@ -849,8 +849,8 @@ reltimein(PG_FUNCTION_ARGS)
ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (strlen(str) > MAXDATELEN)
elog(ERROR, "Bad (length) reltime external representation '%s'", str);
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad reltime external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.68 2002/06/20 20:29:38 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.69 2002/08/04 06:44:47 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -65,7 +65,10 @@ timestamp_in(PG_FUNCTION_ARGS)
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad timestamp external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
......@@ -251,7 +254,11 @@ timestamptz_in(PG_FUNCTION_ARGS)
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad timestamp with time zone"
" external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
......@@ -359,7 +366,7 @@ interval_in(PG_FUNCTION_ARGS)
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
tm->tm_year = 0;
tm->tm_mon = 0;
......@@ -369,6 +376,9 @@ interval_in(PG_FUNCTION_ARGS)
tm->tm_sec = 0;
fsec = 0;
if (strlen(str) >= sizeof(lowstr))
elog(ERROR, "Bad interval external representation (too long) '%s'", str);
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
elog(ERROR, "Bad interval external representation '%s'", str);
......@@ -436,8 +446,6 @@ interval_scale(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
#define MASK(b) (1 << (b))
static void
AdjustIntervalForTypmod(Interval *interval, int32 typmod)
{
......@@ -483,31 +491,34 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
};
#endif
/* Unspecified range and precision? Then not necessary to adjust.
* Setting typmod to -1 is the convention for all types.
*/
if (typmod != -1)
{
int range = ((typmod >> 16) & 0x7FFF);
int precision = (typmod & 0xFFFF);
int range = INTERVAL_RANGE(typmod);
int precision = INTERVAL_PRECISION(typmod);
if (range == 0x7FFF)
if (range == INTERVAL_FULL_RANGE)
{
/* Do nothing... */
}
else if (range == MASK(YEAR))
else if (range == INTERVAL_MASK(YEAR))
{
interval->month = ((interval->month / 12) * 12);
interval->time = 0;
}
else if (range == MASK(MONTH))
else if (range == INTERVAL_MASK(MONTH))
{
interval->month %= 12;
interval->time = 0;
}
/* YEAR TO MONTH */
else if (range == (MASK(YEAR) | MASK(MONTH)))
else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
{
interval->time = 0;
}
else if (range == MASK(DAY))
else if (range == INTERVAL_MASK(DAY))
{
interval->month = 0;
#ifdef HAVE_INT64_TIMESTAMP
......@@ -517,7 +528,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
interval->time = (((int) (interval->time / 86400)) * 86400);
#endif
}
else if (range == MASK(HOUR))
else if (range == INTERVAL_MASK(HOUR))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 day;
......@@ -536,7 +547,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
interval->time = (((int) (interval->time / 3600)) * 3600.0);
#endif
}
else if (range == MASK(MINUTE))
else if (range == INTERVAL_MASK(MINUTE))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 hour;
......@@ -555,7 +566,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
interval->time = (((int) (interval->time / 60)) * 60);
#endif
}
else if (range == MASK(SECOND))
else if (range == INTERVAL_MASK(SECOND))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 minute;
......@@ -573,7 +584,8 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
#endif
}
/* DAY TO HOUR */
else if (range == (MASK(DAY) | MASK(HOUR)))
else if (range == (INTERVAL_MASK(DAY) |
INTERVAL_MASK(HOUR)))
{
interval->month = 0;
#ifdef HAVE_INT64_TIMESTAMP
......@@ -584,7 +596,9 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
#endif
}
/* DAY TO MINUTE */
else if (range == (MASK(DAY) | MASK(HOUR) | MASK(MINUTE)))
else if (range == (INTERVAL_MASK(DAY) |
INTERVAL_MASK(HOUR) |
INTERVAL_MASK(MINUTE)))
{
interval->month = 0;
#ifdef HAVE_INT64_TIMESTAMP
......@@ -595,12 +609,16 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
#endif
}
/* DAY TO SECOND */
else if (range == (MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND)))
else if (range == (INTERVAL_MASK(DAY) |
INTERVAL_MASK(HOUR) |
INTERVAL_MASK(MINUTE) |
INTERVAL_MASK(SECOND)))
{
interval->month = 0;
}
/* HOUR TO MINUTE */
else if (range == (MASK(HOUR) | MASK(MINUTE)))
else if (range == (INTERVAL_MASK(HOUR) |
INTERVAL_MASK(MINUTE)))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 day;
......@@ -620,7 +638,9 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
#endif
}
/* HOUR TO SECOND */
else if (range == (MASK(HOUR) | MASK(MINUTE) | MASK(SECOND)))
else if (range == (INTERVAL_MASK(HOUR) |
INTERVAL_MASK(MINUTE) |
INTERVAL_MASK(SECOND)))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 day;
......@@ -637,7 +657,8 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
#endif
}
/* MINUTE TO SECOND */
else if (range == (MASK(MINUTE) | MASK(SECOND)))
else if (range == (INTERVAL_MASK(MINUTE) |
INTERVAL_MASK(SECOND)))
{
#ifdef HAVE_INT64_TIMESTAMP
int64 hour;
......@@ -657,7 +678,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
elog(ERROR, "AdjustIntervalForTypmod(): internal coding error");
/* Need to adjust precision? If not, don't even try! */
if (precision != 0xFFFF)
if (precision != INTERVAL_FULL_PRECISION)
{
if ((precision < 0) || (precision > MAX_INTERVAL_PRECISION))
elog(ERROR, "INTERVAL(%d) precision must be between %d and %d",
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.86 2002/06/20 20:29:38 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.87 2002/08/04 06:44:47 thomas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -550,7 +550,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
/*
* Unfortunately, there is no strncoll(), so in the non-C locale
* case we have to do some memory copying. This turns out to be
* significantly slower, so we optimize the case were LC_COLLATE
* significantly slower, so we optimize the case where LC_COLLATE
* is C.
*/
if (!lc_collate_is_c())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment