@@ -32,8 +32,16 @@ static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 3
32
32
static void do_range_limit (timelib_sll start , timelib_sll end , timelib_sll adj , timelib_sll * a , timelib_sll * b )
33
33
{
34
34
if (* a < start ) {
35
- * b -= (start - * a - 1 ) / adj + 1 ;
36
- * a += adj * ((start - * a - 1 ) / adj + 1 );
35
+ /* We calculate 'a + 1' first as 'start - *a - 1' causes an int64_t overflows if *a is
36
+ * LONG_MIN. 'start' is 0 in this context, and '0 - LONG_MIN > LONG_MAX'. */
37
+ timelib_sll a_plus_1 = * a + 1 ;
38
+
39
+ * b -= (start - a_plus_1 ) / adj + 1 ;
40
+
41
+ /* This code add the extra 'adj' separately, as otherwise this can overflow int64_t in
42
+ * situations where *b is near LONG_MIN. */
43
+ * a += adj * ((start - a_plus_1 ) / adj );
44
+ * a += adj ;
37
45
}
38
46
if (* a >= end ) {
39
47
* b += * a / adj ;
@@ -462,9 +470,15 @@ void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
462
470
do_adjust_relative (time );
463
471
do_adjust_special (time );
464
472
465
- time -> sse =
466
- (timelib_epoch_days_from_time (time ) * SECS_PER_DAY ) +
467
- timelib_hms_to_seconds (time -> h , time -> i , time -> s );
473
+ /* You might be wondering, why this code does this in two steps. This is because
474
+ * timelib_epoch_days_from_time(time) * SECS_PER_DAY with the lowest limit of
475
+ * timelib_epoch_days_from_time() is less than the range of an int64_t. This then overflows. In
476
+ * order to be able to still allow for any time in that day that only halfly fits in the int64_t
477
+ * range, we add the time element first, which is always positive, and then twice half the value
478
+ * of the earliest day as expressed as unix timestamp. */
479
+ time -> sse = timelib_hms_to_seconds (time -> h , time -> i , time -> s );
480
+ time -> sse += timelib_epoch_days_from_time (time ) * (SECS_PER_DAY / 2 );
481
+ time -> sse += timelib_epoch_days_from_time (time ) * (SECS_PER_DAY / 2 );
468
482
469
483
// This modifies time->sse, if needed
470
484
do_adjust_timezone (time , tzi );
0 commit comments