* NEWS, time2posix.3: Mention this.
* localtime.c (decrement_overflow_time): New function.
(time2posix_z): Use it.
(time2posix_z, posix2time_z): If the result cannot be represented,
set errno=EOVERFLOW and return -1 instead of having undefined
behavior which in practice could involve inflooping and crashing.
---
 NEWS         |  9 +++++++
 localtime.c  | 74 ++++++++++++++++++++++++++++++++++------------------
 time2posix.3 | 23 +++++++++++++++-
 3 files changed, 79 insertions(+), 27 deletions(-)

diff --git a/NEWS b/NEWS
index 539e5968..71bd91e4 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Unreleased, experimental changes
 
   Briefly:
     The "right" TZif files are no longer installed by default.
+    Several integer overflow bugs have been fixed.
 
   Changes to build procedure
 
@@ -30,6 +31,14 @@ Unreleased, experimental changes
     obsolete in release 2019b, and fixes some undefined behavior.
     (Undefined behavior reported by GitHub user Naveed8951.)
 
+    The posix2time, posix2time_z, time2posix, and time2posix_z
+    functions now set errno=EOVERFLOW and return ((time_t) -1) if the
+    result is not representable.  Formerly they had undefined behavior
+    that could in practice result in crashing, looping indefinitely,
+    or returning an incorrect result.  As before, these functions are
+    defined only when localtime.c is compiled with the -DSTD_INSPIRED
+    option.
+
     Some other undefined behavior, triggered by TZif files containing
     outlandish but conforming UT offsets or leap second corrections,
     has also been fixed.  (Also reported by Naveed8951.)
diff --git a/localtime.c b/localtime.c
index 16eec790..aa9b48ca 100644
--- a/localtime.c
+++ b/localtime.c
@@ -479,7 +479,7 @@ struct ttinfo {                             /* time type 
information */
 };
 
 struct lsinfo {                                /* leap second information */
-       time_t          ls_trans;       /* transition time */
+       time_t          ls_trans;       /* transition time (positive) */
        int_fast32_2s   ls_corr;        /* correction to apply */
 };
 
@@ -2967,6 +2967,21 @@ leapcorr(struct state const *sp, time_t t)
 #if !USE_TIMEX_T
 # if STD_INSPIRED
 
+static bool
+decrement_overflow_time(time_t *tp, int_fast32_2s j)
+{
+#ifdef ckd_sub
+  return ckd_sub(tp, *tp, j);
+#else
+  if (! (j < 0
+        ? *tp <= TIME_T_MAX + j
+        : (TYPE_SIGNED(time_t) ? TIME_T_MIN + j <= *tp : j <= *tp)))
+    return true;
+  *tp -= j;
+  return false;
+#endif
+}
+
 /* NETBSD_INSPIRED_EXTERN functions are exported to callers if
    NETBSD_INSPIRED is defined, and are private otherwise.  */
 #  if NETBSD_INSPIRED
@@ -2986,7 +3001,13 @@ leapcorr(struct state const *sp, time_t t)
 NETBSD_INSPIRED_EXTERN time_t
 time2posix_z(struct state *sp, time_t t)
 {
-  return t - leapcorr(sp, t);
+  if (decrement_overflow_time(&t, leapcorr(sp, t))) {
+    /* Overflow near maximum time_t value with negative correction.
+       This can happen with unrealistic-but-valid TZif files.  */
+    errno = EOVERFLOW;
+    return -1;
+  }
+  return t;
 }
 
 time_t
@@ -3009,30 +3030,31 @@ time2posix(time_t t)
 NETBSD_INSPIRED_EXTERN time_t
 posix2time_z(struct state *sp, time_t t)
 {
-       time_t  x;
-       time_t  y;
-       /*
-       ** For a positive leap second hit, the result
-       ** is not unique. For a negative leap second
-       ** hit, the corresponding time doesn't exist,
-       ** so we return an adjacent second.
-       */
-       x = t + leapcorr(sp, t);
-       y = x - leapcorr(sp, x);
-       if (y < t) {
-               do {
-                       x++;
-                       y = x - leapcorr(sp, x);
-               } while (y < t);
-               x -= y != t;
-       } else if (y > t) {
-               do {
-                       --x;
-                       y = x - leapcorr(sp, x);
-               } while (y > t);
-               x += y != t;
-       }
-       return x;
+  int i;
+  for (i = sp->leapcnt; 0 <= --i; ) {
+    struct lsinfo *lp = &sp->lsis[i];
+    int_fast32_2s corr = lp->ls_corr;
+    time_t t_corr = t;
+
+    if (increment_overflow_time(&t_corr, corr)) {
+      if (0 <= corr) {
+       /* Overflow near maximum time_t value with positive correction.
+          This can happen with ordinary TZif files with leap seconds.  */
+       errno = EOVERFLOW;
+       return -1;
+      } else {
+       /* A negative correction overflowed, so keep going.
+          This can happen with unrealistic-but-valid TZif files.  */
+      }
+    } else {
+      time_t trans = lp->ls_trans;
+      if (trans <= t_corr)
+       return (t_corr
+               - (trans == t_corr
+                  && (i == 0 ? 0 : sp->lsis[i - 1].ls_corr) < corr));
+    }
+  }
+  return t;
 }
 
 time_t
diff --git a/time2posix.3 b/time2posix.3
index adc6c65c..adbff83a 100644
--- a/time2posix.3
+++ b/time2posix.3
@@ -145,8 +145,29 @@ and
 .B posix2time
 degenerate to the identity function.
 .SH "RETURN VALUE"
-These functions return the resulting timestamp without modifying
+If successful, these functions return the resulting timestamp without modifying
 .BR errno .
+Otherwise, they set
+.B errno
+and return
+.BR "((time_t) -1)" .
+.SH ERRORS
+These functions fail if:
+.TP
+[EOVERFLOW]
+The resulting value cannot be represented.
+This can happen for
+.B posix2time
+if its argument is close to the maximum
+.B time_t
+value.
+In environments where the
+.I TZ
+environment variable names a TZif file,
+overflow can happen for either function for an argument sufficiently
+close to an extreme
+.B time_t
+value if the TZif file specifies unrealistic leap second corrections.
 .SH SEE ALSO
 .BR difftime (3),
 .BR localtime (3),
-- 
2.51.0

Reply via email to