]> granicus.if.org Git - postgresql/commitdiff
Reject out-of-range numeric timezone specifications.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Jul 2014 02:41:36 +0000 (22:41 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Jul 2014 02:41:36 +0000 (22:41 -0400)
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.)

src/backend/commands/variable.c
src/timezone/pgtz.c

index 26378da8279c4c50878e2ad3084676a5b9f3fe1e..74f5437f01c0f0914c34ce995905941b0b1ea4c5 100644 (file)
@@ -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)
index 5c28c5c483d74f8968d893f37cc463d72c242764..de1832ab6d2d6a79d625aebfdb30a7b1b26ce4ab 100644 (file)
@@ -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)