summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2014-07-22 02:41:36 +0000
committerTom Lane2014-07-22 02:41:36 +0000
commit6e5a39c9e6e17c55570e246373c55f3896f52e5e (patch)
tree3105c257401064ee5f8c96aa437b4e2f5e95b964
parent391aa8aac11fe78f3b2f5743b56e78b0ad6a5d84 (diff)
Reject out-of-range numeric timezone specifications.
In commit 631dc390f49909a5c8ebd6002cfb2bcee5415a9d, we started to handle simple numeric timezone offsets via the zic library instead of the old CTimeZone/HasCTZSet kluge. However, we overlooked the fact that the zic code will reject UTC offsets exceeding a week (which seems a bit arbitrary, but not because it's too tight ...). This led to possibly setting session_timezone to NULL, which results in crashes in most timezone-related operations as of 9.4, and crashes in a small number of places even before that. So check for NULL return from pg_tzset_offset() and report an appropriate error message. Per bug #11014 from Duncan Gillis. Back-patch to all supported branches, like the previous patch. (Unfortunately, as of today that no longer includes 8.4.)
-rw-r--r--src/backend/commands/variable.c45
-rw-r--r--src/timezone/pgtz.c3
2 files changed, 37 insertions, 11 deletions
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 26378da8279..74f5437f01c 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -241,6 +241,8 @@ assign_timezone(const char *value, bool doit, GucSource source)
char *result;
char *endptr;
double hours;
+ int new_ctimezone;
+ pg_tz *new_tz;
/*
* Check for INTERVAL 'foo'
@@ -294,16 +296,28 @@ assign_timezone(const char *value, bool doit, GucSource source)
pfree(interval);
return NULL;
}
- if (doit)
- {
- /* Here we change from SQL to Unix sign convention */
+
+ /* Here we change from SQL to Unix sign convention */
#ifdef HAVE_INT64_TIMESTAMP
- CTimeZone = -(interval->time / USECS_PER_SEC);
+ new_ctimezone = -(interval->time / USECS_PER_SEC);
#else
- CTimeZone = -interval->time;
+ new_ctimezone = -interval->time;
#endif
- session_timezone = pg_tzset_offset(CTimeZone);
+ new_tz = pg_tzset_offset(new_ctimezone);
+ if (!new_tz)
+ {
+ ereport(GUC_complaint_elevel(source),
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid interval value for time zone: out of range")));
+ pfree(interval);
+ return NULL;
+ }
+
+ if (doit)
+ {
+ CTimeZone = new_ctimezone;
+ session_timezone = new_tz;
HasCTZSet = true;
}
pfree(interval);
@@ -316,11 +330,22 @@ assign_timezone(const char *value, bool doit, GucSource source)
hours = strtod(value, &endptr);
if (endptr != value && *endptr == '\0')
{
+ /* Here we change from SQL to Unix sign convention */
+ new_ctimezone = -hours * SECS_PER_HOUR;
+ new_tz = pg_tzset_offset(new_ctimezone);
+
+ if (!new_tz)
+ {
+ ereport(GUC_complaint_elevel(source),
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for time zone: out of range")));
+ return NULL;
+ }
+
if (doit)
{
- /* Here we change from SQL to Unix sign convention */
- CTimeZone = -hours * SECS_PER_HOUR;
- session_timezone = pg_tzset_offset(CTimeZone);
+ CTimeZone = new_ctimezone;
+ session_timezone = new_tz;
HasCTZSet = true;
}
}
@@ -352,8 +377,6 @@ assign_timezone(const char *value, bool doit, GucSource source)
/*
* Otherwise assume it is a timezone name, and try to load it.
*/
- pg_tz *new_tz;
-
new_tz = pg_tzset(value);
if (!new_tz)
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index 5c28c5c483d..de1832ab6d2 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -1333,6 +1333,9 @@ pg_tzset(const char *name)
* The GMT offset is specified in seconds, positive values meaning west of
* Greenwich (ie, POSIX not ISO sign convention). However, we use ISO
* sign convention in the displayable abbreviation for the zone.
+ *
+ * Caution: this can fail (return NULL) if the specified offset is outside
+ * the range allowed by the zic library.
*/
pg_tz *
pg_tzset_offset(long gmtoffset)