From 51f471432715f7a1880d38c135ce41182b4dc78e Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 5 Nov 2026 10:00:32 +0100 Subject: 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 --- src/time64.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') 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 #include #include +#include #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 -- cgit v1.1-32-gdbae