* 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