summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-03-26 11:42:58 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-03-26 11:42:58 -0400
commit7e25b9c6e718437ed45aa2592598c63f0f87e70a (patch)
tree2a0caae879d2801c1c76414a197b8796ee87a26b
parentdc885d2e4cb12d3a3444cea7f754a691f6d200c4 (diff)
downloadbfs-7e25b9c6e718437ed45aa2592598c63f0f87e70a.tar.xz
xtime: Don't update tm if xtimegm() overflows
-rw-r--r--src/xtime.c62
-rw-r--r--tests/xtime.c34
2 files changed, 55 insertions, 41 deletions
diff --git a/src/xtime.c b/src/xtime.c
index 5b259ab..bcf6dd3 100644
--- a/src/xtime.c
+++ b/src/xtime.c
@@ -69,73 +69,77 @@ static int month_length(int year, int month) {
}
int xtimegm(struct tm *tm, time_t *timep) {
- tm->tm_isdst = 0;
+ struct tm copy = *tm;
+ copy.tm_isdst = 0;
- if (wrap(&tm->tm_sec, 60, &tm->tm_min) != 0) {
+ if (wrap(&copy.tm_sec, 60, &copy.tm_min) != 0) {
goto overflow;
}
- if (wrap(&tm->tm_min, 60, &tm->tm_hour) != 0) {
+ if (wrap(&copy.tm_min, 60, &copy.tm_hour) != 0) {
goto overflow;
}
- if (wrap(&tm->tm_hour, 24, &tm->tm_mday) != 0) {
+ if (wrap(&copy.tm_hour, 24, &copy.tm_mday) != 0) {
goto overflow;
}
// In order to wrap the days of the month, we first need to know what
// month it is
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
+ if (wrap(&copy.tm_mon, 12, &copy.tm_year) != 0) {
goto overflow;
}
- if (tm->tm_mday < 1) {
+ if (copy.tm_mday < 1) {
do {
- --tm->tm_mon;
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
+ --copy.tm_mon;
+ if (wrap(&copy.tm_mon, 12, &copy.tm_year) != 0) {
goto overflow;
}
- tm->tm_mday += month_length(tm->tm_year, tm->tm_mon);
- } while (tm->tm_mday < 1);
+ copy.tm_mday += month_length(copy.tm_year, copy.tm_mon);
+ } while (copy.tm_mday < 1);
} else {
while (true) {
- int days = month_length(tm->tm_year, tm->tm_mon);
- if (tm->tm_mday <= days) {
+ int days = month_length(copy.tm_year, copy.tm_mon);
+ if (copy.tm_mday <= days) {
break;
}
- tm->tm_mday -= days;
- ++tm->tm_mon;
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
+ copy.tm_mday -= days;
+ ++copy.tm_mon;
+ if (wrap(&copy.tm_mon, 12, &copy.tm_year) != 0) {
goto overflow;
}
}
}
- tm->tm_yday = 0;
- for (int i = 0; i < tm->tm_mon; ++i) {
- tm->tm_yday += month_length(tm->tm_year, i);
+ copy.tm_yday = 0;
+ for (int i = 0; i < copy.tm_mon; ++i) {
+ copy.tm_yday += month_length(copy.tm_year, i);
}
- tm->tm_yday += tm->tm_mday - 1;
+ copy.tm_yday += copy.tm_mday - 1;
int leap_days;
// Compute floor((year - 69)/4) - floor((year - 1)/100) + floor((year + 299)/400) without overflows
- if (tm->tm_year >= 0) {
- leap_days = floor_div(tm->tm_year - 69, 4) - floor_div(tm->tm_year - 1, 100) + floor_div(tm->tm_year - 101, 400) + 1;
+ if (copy.tm_year >= 0) {
+ leap_days = floor_div(copy.tm_year - 69, 4) - floor_div(copy.tm_year - 1, 100) + floor_div(copy.tm_year - 101, 400) + 1;
} else {
- leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17;
+ leap_days = floor_div(copy.tm_year + 3, 4) - floor_div(copy.tm_year + 99, 100) + floor_div(copy.tm_year + 299, 400) - 17;
}
- long long epoch_days = 365LL * (tm->tm_year - 70) + leap_days + tm->tm_yday;
- tm->tm_wday = (epoch_days + 4) % 7;
- if (tm->tm_wday < 0) {
- tm->tm_wday += 7;
+ long long epoch_days = 365LL * (copy.tm_year - 70) + leap_days + copy.tm_yday;
+ copy.tm_wday = (epoch_days + 4) % 7;
+ if (copy.tm_wday < 0) {
+ copy.tm_wday += 7;
}
- long long epoch_time = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * epoch_days));
- *timep = (time_t)epoch_time;
- if ((long long)*timep != epoch_time) {
+ long long epoch_time = copy.tm_sec + 60 * (copy.tm_min + 60 * (copy.tm_hour + 24 * epoch_days));
+ time_t time = (time_t)epoch_time;
+ if ((long long)time != epoch_time) {
goto overflow;
}
+
+ *tm = copy;
+ *timep = time;
return 0;
overflow:
diff --git a/tests/xtime.c b/tests/xtime.c
index c8dc00b..ec499d8 100644
--- a/tests/xtime.c
+++ b/tests/xtime.c
@@ -137,6 +137,24 @@ static bool check_one_xtimegm(const struct tm *tm) {
return ret;
}
+/** Check an overflowing xtimegm() call. */
+static bool check_xtimegm_overflow(const struct tm *tm) {
+ struct tm copy = *tm;
+ time_t time = 123;
+
+ bool ret = true;
+ ret &= bfs_check(xtimegm(&copy, &time) == -1 && errno == EOVERFLOW);
+ ret &= bfs_check(tm_equal(&copy, tm));
+ ret &= bfs_check(time == 123);
+
+ if (!ret) {
+ bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(copy));
+ bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*tm));
+ }
+
+ return ret;
+}
+
/** xtimegm() tests. */
static bool check_xtimegm(void) {
bool ret = true;
@@ -144,7 +162,6 @@ static bool check_xtimegm(void) {
struct tm tm = {
.tm_isdst = -1,
};
- time_t time;
// Check equivalence with mktime()
for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10)
@@ -157,17 +174,10 @@ static bool check_xtimegm(void) {
}
// Check integer overflow cases
- tm = (struct tm){ .tm_sec = INT_MAX, .tm_min = INT_MAX };
- ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW);
-
- tm = (struct tm){ .tm_min = INT_MAX, .tm_hour = INT_MAX };
- ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW);
-
- tm = (struct tm){ .tm_hour = INT_MAX, .tm_mday = INT_MAX };
- ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW);
-
- tm = (struct tm){ .tm_mon = INT_MAX, .tm_year = INT_MAX };
- ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW);
+ check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
return ret;
}