diff options
| author | 2026-11-05 10:00:32 +0100 | |
|---|---|---|
| committer | 2026-11-05 10:00:32 +0100 | |
| commit | 51f471432715f7a1880d38c135ce41182b4dc78e (patch) | |
| tree | 131610d91ca3672b7eb6499d6ab778c4bba51636 | |
| parent | 5f22030bf57d2f0c78eb26b9778fd3180cfb70f5 (diff) | |
| download | libplist-51f471432715f7a1880d38c135ce41182b4dc78e.tar.gz libplist-51f471432715f7a1880d38c135ce41182b4dc78e.tar.bz2 | |
time64: fix signed overflow for extreme years in gmtime64_r()
IS_LEAP() computed `tm_year + 1900` in int arithmetic, which overflows
for dates around year 2^31 (hit via check_tm() when writing dates).
Cast to Year (64-bit) in IS_LEAP(). Also make gmtime64_r() return NULL
with EOVERFLOW when the calendar year (tm_year + 1900) does not fit in
an int; the previous check compared tm_year with itself and could never
fail. This avoids strftime() wrapping the year in the output, and
callers fall back to their existing default date string.
Credit to OSSFuzz
| -rw-r--r-- | src/time64.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/src/time64.c b/src/time64.c index 218088e..6a9218c 100644 --- a/src/time64.c +++ b/src/time64.c @@ -45,6 +45,7 @@ gmtime64_r() is a 64-bit equivalent of gmtime_r(). #include <string.h> #include <time.h> #include <errno.h> +#include <limits.h> #include "time64.h" #include "time64_limits.h" @@ -125,7 +126,7 @@ static const char dow_year_start[SOLAR_CYCLE_LENGTH] = { #define CHEAT_DAYS (1199145600 / 24 / 60 / 60) #define CHEAT_YEARS 108 -#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) +#define IS_LEAP(n) ((!(((Year)(n) + 1900) % 400) || (!(((Year)(n) + 1900) % 4) && (((Year)(n) + 1900) % 100))) != 0) #define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) #ifdef USE_SYSTEM_LOCALTIME @@ -649,7 +650,8 @@ struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p) } p->tm_year = year; - if( p->tm_year != year ) { + /* The calendar year (tm_year + 1900) must also fit in an int, e.g. for strftime() */ + if( p->tm_year != year || year > (Year)INT_MAX - 1900 || year < (Year)INT_MIN ) { #ifdef EOVERFLOW errno = EOVERFLOW; #endif |
