Skip to content

Commit 6e5a39c

Browse files
committed
Reject out-of-range numeric timezone specifications.
In commit 631dc39, 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.)
1 parent 391aa8a commit 6e5a39c

File tree

2 files changed

+37
-11
lines changed

2 files changed

+37
-11
lines changed

src/backend/commands/variable.c

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ assign_timezone(const char *value, bool doit, GucSource source)
241241
char *result;
242242
char *endptr;
243243
double hours;
244+
int new_ctimezone;
245+
pg_tz *new_tz;
244246

245247
/*
246248
* Check for INTERVAL 'foo'
@@ -294,16 +296,28 @@ assign_timezone(const char *value, bool doit, GucSource source)
294296
pfree(interval);
295297
return NULL;
296298
}
297-
if (doit)
298-
{
299-
/* Here we change from SQL to Unix sign convention */
299+
300+
/* Here we change from SQL to Unix sign convention */
300301
#ifdef HAVE_INT64_TIMESTAMP
301-
CTimeZone = -(interval->time / USECS_PER_SEC);
302+
new_ctimezone = -(interval->time / USECS_PER_SEC);
302303
#else
303-
CTimeZone = -interval->time;
304+
new_ctimezone = -interval->time;
304305
#endif
305-
session_timezone = pg_tzset_offset(CTimeZone);
306+
new_tz = pg_tzset_offset(new_ctimezone);
306307

308+
if (!new_tz)
309+
{
310+
ereport(GUC_complaint_elevel(source),
311+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
312+
errmsg("invalid interval value for time zone: out of range")));
313+
pfree(interval);
314+
return NULL;
315+
}
316+
317+
if (doit)
318+
{
319+
CTimeZone = new_ctimezone;
320+
session_timezone = new_tz;
307321
HasCTZSet = true;
308322
}
309323
pfree(interval);
@@ -316,11 +330,22 @@ assign_timezone(const char *value, bool doit, GucSource source)
316330
hours = strtod(value, &endptr);
317331
if (endptr != value && *endptr == '\0')
318332
{
333+
/* Here we change from SQL to Unix sign convention */
334+
new_ctimezone = -hours * SECS_PER_HOUR;
335+
new_tz = pg_tzset_offset(new_ctimezone);
336+
337+
if (!new_tz)
338+
{
339+
ereport(GUC_complaint_elevel(source),
340+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
341+
errmsg("invalid value for time zone: out of range")));
342+
return NULL;
343+
}
344+
319345
if (doit)
320346
{
321-
/* Here we change from SQL to Unix sign convention */
322-
CTimeZone = -hours * SECS_PER_HOUR;
323-
session_timezone = pg_tzset_offset(CTimeZone);
347+
CTimeZone = new_ctimezone;
348+
session_timezone = new_tz;
324349
HasCTZSet = true;
325350
}
326351
}
@@ -352,8 +377,6 @@ assign_timezone(const char *value, bool doit, GucSource source)
352377
/*
353378
* Otherwise assume it is a timezone name, and try to load it.
354379
*/
355-
pg_tz *new_tz;
356-
357380
new_tz = pg_tzset(value);
358381

359382
if (!new_tz)

src/timezone/pgtz.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,9 @@ pg_tzset(const char *name)
13331333
* The GMT offset is specified in seconds, positive values meaning west of
13341334
* Greenwich (ie, POSIX not ISO sign convention). However, we use ISO
13351335
* sign convention in the displayable abbreviation for the zone.
1336+
*
1337+
* Caution: this can fail (return NULL) if the specified offset is outside
1338+
* the range allowed by the zic library.
13361339
*/
13371340
pg_tz *
13381341
pg_tzset_offset(long gmtoffset)

0 commit comments

Comments
 (0)