From 9bd48f30d544f435d2ad137d1d5beb098c579c10 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 18 May 2026 09:15:46 -0400 Subject: [PATCH 1/2] ext/date: build with -fwrapv to define signed-overflow wrap behavior Year arithmetic in timelib (tm2unixtime) overflows int64_t under extreme inputs such as PHP_INT_MIN years. The result is undefined behavior detected by UBSan. Compile ext/date with -fwrapv so the wrap is well-defined. --- ext/date/config0.m4 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/date/config0.m4 b/ext/date/config0.m4 index 5af6be13faf7..9bd80e464f82 100644 --- a/ext/date/config0.m4 +++ b/ext/date/config0.m4 @@ -9,6 +9,10 @@ AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], [-Werror]) PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DHAVE_TIMELIB_CONFIG_H=1" + +AX_CHECK_COMPILE_FLAG([-fwrapv], + [PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -fwrapv"]) + timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c lib/parse_posix.c lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c" From 134769d775b7931a6f90140f317eefef29ede8b5 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 4 Apr 2026 09:55:02 -0400 Subject: [PATCH 2/2] Fix GH-18422: int overflow in php_date_llabs php_date_llabs negated its argument with -i, which is UB when i is LLONG_MIN. Cast to uint64_t before negating and use stdint types throughout. Updated Y/x/X format call sites to PRIu64. Closes GH-18422 --- NEWS | 3 +++ ext/date/php_date.c | 14 ++++---------- ext/date/tests/gh18422.phpt | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 ext/date/tests/gh18422.phpt diff --git a/NEWS b/NEWS index 3413ff97c2b5..ce2b34730978 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.22 +- Date: + . Fixed bug GH-18422 (int overflow in php_date_llabs). (iliaal) + - Intl: . Fix incorrect argument positions in out-of-bounds errors for IntlCalendar::set(), IntlCalendar::setDate(), IntlCalendar::setDateTime(), diff --git a/ext/date/php_date.c b/ext/date/php_date.c index acdd612d04c8..2fbfe6e14f7e 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -31,13 +31,7 @@ #include "win32/time.h" #endif -#ifdef PHP_WIN32 -static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; } -#elif defined(__GNUC__) && __GNUC__ < 3 -static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; } -#else -static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; } -#endif +static inline uint64_t php_date_llabs(int64_t i) { return i >= 0 ? (uint64_t)i : -(uint64_t)i; } #ifdef PHP_WIN32 #define DATE_I64_BUF_LEN 65 @@ -742,9 +736,9 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t /* year */ case 'L': length = slprintf(buffer, sizeof(buffer), "%d", timelib_is_leap((int) t->y)); break; case 'y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) (t->y % 100)); break; - case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; - case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; - case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; + case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; + case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; + case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; /* time */ case 'a': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "pm" : "am"); break; diff --git a/ext/date/tests/gh18422.phpt b/ext/date/tests/gh18422.phpt new file mode 100644 index 000000000000..643476615277 --- /dev/null +++ b/ext/date/tests/gh18422.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-18422 (int overflow in Date extension) +--FILE-- +format("Y"), "\n"; +echo $dto->format("x"), "\n"; +echo $dto->format("X"), "\n"; + +echo date_create("2024-06-15")->format("Y"), "\n"; +echo date_create("-0042-01-01")->format("Y"), "\n"; +?> +--EXPECTF-- +-%d +-%d +-%d +2024 +-0042