* 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