Commit 09ca095aa8e for php.net

commit 09ca095aa8e5de7ce23f82b116d7def76d75785b
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon May 18 14:31:10 2026 -0400

    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.

    Fixes GH-18422
    Closes GH-21638

diff --git a/NEWS b/NEWS
index 3413ff97c2b..ce2b3473097 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/config0.m4 b/ext/date/config0.m4
index 5af6be13faf..9bd80e464f8 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"

diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index acdd612d04c..2fbfe6e14f7 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 00000000000..64347661527
--- /dev/null
+++ b/ext/date/tests/gh18422.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-18422 (int overflow in Date extension)
+--FILE--
+<?php
+date_default_timezone_set('UTC');
+
+$dto = date_create("2006-12-12");
+date_isodate_set($dto, PHP_INT_MIN, 1, 1);
+echo $dto->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