Skip to content

Commit

Permalink
icaltime: Fails to convert time to time_t before epoch
Browse files Browse the repository at this point in the history
The time_t of a time before epoch is a negative number, but the code
considered a negative number as an incorrectly entered time.
  • Loading branch information
mcrha committed Nov 8, 2024
1 parent 7b096e2 commit 0c4a35b
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 11 deletions.
1 change: 1 addition & 0 deletions ReleaseNotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Version 3.0.19 (UNRELEASED):
* Improved libicu discrovery on Mac with homebrew
* Properly set DYLD_LIBRARY_PATH on Mac for libical-ical tests
* Resolved known limitation: Negative values are now also supported for `BYMONTHDAY` and `BYYEARDAY`.
* Fix time conversion to time_t for times before epoch

Version 3.0.18 (31 March 2024):
-------------------------------
Expand Down
29 changes: 18 additions & 11 deletions src/libical/icaltime.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@ static int icaltime_leap_days(int y1, int y2)
* in that we don't want the automatic adjustments for
* local daylight savings time applied to the result.
* This function expects well-formed input.
*
* The out_time_t is to store the result, it can be NULL.
* Returns 0 on failure, 1 on success.
*/
static time_t make_time(const struct tm *tm, int tzm)
static int make_time(const struct tm *tm, int tzm, time_t *out_time_t)
{
time_t tim;
int febs;
Expand All @@ -91,29 +94,29 @@ static time_t make_time(const struct tm *tm, int tzm)
/* check that month specification within range */

if (tm->tm_mon < 0 || tm->tm_mon > 11)
return ((time_t) - 1);
return 0;

if (tm->tm_year < 2)
return ((time_t)-1);
return 0;

#if (SIZEOF_TIME_T == 4)
/* check that year specification within range */

if (tm->tm_year > 138)
return ((icaltime_t)-1);
return 0;

/* check for upper bound of Jan 17, 2038 (to avoid possibility of 32-bit arithmetic overflow) */
if (tm->tm_year == 138) {
if (tm->tm_mon > 0) {
return ((time_t) - 1);
return 0;
} else if (tm->tm_mday > 17) {
return ((time_t) - 1);
return 0;
}
}
#else
/* We don't support years >= 10000, because the function has not been tested at this range. */
if (tm->tm_year >= 8100) {
return ((time_t)-1);
return 0;
}
#endif /* SIZEOF_TIME_T */

Expand Down Expand Up @@ -162,7 +165,10 @@ static time_t make_time(const struct tm *tm, int tzm)

/* return number of seconds since start of the epoch */

return (tim);
if (out_time_t)
*out_time_t = tim;

return 1;
}

/*
Expand All @@ -177,7 +183,7 @@ static time_t icaltime_timegm(const struct tm *tm)
time_t seconds;

/* Validate the tm structure by passing it through make_time() */
if (make_time(tm, 0) < 0) {
if (!make_time(tm, 0, NULL)) {
/* we have some invalid data in the tm struct */
return 0;
}
Expand Down Expand Up @@ -249,7 +255,7 @@ struct icaltimetype icaltime_today(void)
time_t icaltime_as_timet(const struct icaltimetype tt)
{
struct tm stm;
time_t t;
time_t t = (time_t)-1;

/* If the time is the special null time, return 0. */
if (icaltime_is_null_time(tt)) {
Expand All @@ -272,7 +278,8 @@ time_t icaltime_as_timet(const struct icaltimetype tt)
stm.tm_year = tt.year - 1900;
stm.tm_isdst = -1;

t = make_time(&stm, 0);
if (!make_time(&stm, 0, &t))
t = ((time_t)-1);

return t;
}
Expand Down
21 changes: 21 additions & 0 deletions src/test/regression.c
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,27 @@ void do_test_time(const char *zone)
int_is("icaltime_compare(): same UTC and NY",
icaltime_compare(icttutc, icttny),
0);

/* Conversion to time_t around the epoch */
ictt = icaltime_from_string("19691231T235958Z");
tt = icaltime_as_timet_with_zone(ictt, utczone);
int_is("convert to time_t EPOCH-2", (int)tt, -2);

ictt = icaltime_from_string("19691231T235959Z");
tt = icaltime_as_timet_with_zone(ictt, utczone);
int_is("convert to time_t EPOCH-1", (int)tt, -1);

ictt = icaltime_from_string("19700101T000000Z");
tt = icaltime_as_timet_with_zone(ictt, utczone);
int_is("convert to time_t EPOCH", (int)tt, 0);

ictt = icaltime_from_string("19700101T000001Z");
tt = icaltime_as_timet_with_zone(ictt, utczone);
int_is("convert to time_t EPOCH+1", (int)tt, 1);

ictt = icaltime_from_string("19700101T000002Z");
tt = icaltime_as_timet_with_zone(ictt, utczone);
int_is("convert to time_t EPOCH+2", (int)tt, 2);
}

void test_iterators(void)
Expand Down

0 comments on commit 0c4a35b

Please sign in to comment.