Commit ab5e4d79783 for php

commit ab5e4d79783b0741f0c8b26ba337d23ef75ec1ad
Author: Alexander Danilov <10532067+adapik@users.noreply.github.com>
Date:   Fri Oct 16 12:21:48 2026 +0300

    date: Validate nanoseconds range and reject negative zero duration (#24356)

diff --git a/ext/date/tests/time/duration/serialize.phpt b/ext/date/tests/time/duration/serialize.phpt
index 982a84bef39..c2af67efd6f 100644
--- a/ext/date/tests/time/duration/serialize.phpt
+++ b/ext/date/tests/time/duration/serialize.phpt
@@ -46,6 +46,20 @@
     echo $e::class, ': ', $e->getMessage(), PHP_EOL;
 }

+try {
+    // Nanoseconds that would be truncated to a valid value by a 32-bit conversion (2^32 + 5).
+    unserialize('O:13:"Time\Duration":3:{s:7:"seconds";i:1;s:11:"nanoseconds";i:4294967301;s:8:"negative";b:0;}');
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+
+try {
+    // A negative zero duration is rejected.
+    unserialize('O:13:"Time\Duration":3:{s:7:"seconds";i:0;s:11:"nanoseconds";i:0;s:8:"negative";b:1;}');
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+
 try {
     Time\Duration::fromSeconds(1, 1)
         ->__unserialize([
@@ -69,3 +83,5 @@
 Exception: Invalid serialization data for Time\Duration object
 Exception: Invalid serialization data for Time\Duration object
 Exception: Invalid serialization data for Time\Duration object
+Exception: Invalid serialization data for Time\Duration object
+Exception: Invalid serialization data for Time\Duration object
diff --git a/ext/date/time_duration.c b/ext/date/time_duration.c
index a9f69db7a3d..8565f3e8c5f 100644
--- a/ext/date/time_duration.c
+++ b/ext/date/time_duration.c
@@ -175,11 +175,18 @@ PHP_METHOD(Time_Duration, __unserialize)
 		goto fail;
 	}

-	/* Verify that both properties are positive, since the timelib_duration_ctor_static() takes unsigned. */
+	/* Verify that both properties are non-negative, since the timelib_duration_ctor_static() takes
+	 * unsigned. */
 	if (Z_LVAL_P(seconds) < 0 || Z_LVAL_P(nanoseconds) < 0) {
 		goto fail;
 	}

+	/* timelib_duration_ctor_static() takes a uint32_t for nanoseconds, so larger values would
+	 * implicitly wrap around instead of being rejected. */
+	if (Z_LVAL_P(nanoseconds) >= NANOS_IN_SEC) {
+		goto fail;
+	}
+
 	int error = timelib_duration_ctor_static(&duration->duration, Z_LVAL_P(seconds), Z_LVAL_P(nanoseconds), Z_TYPE_P(negative) == IS_TRUE);
 	if (error != TIMELIB_ERROR_NO_ERROR) {
 		throw_timelib_error(error);
@@ -191,6 +198,10 @@ PHP_METHOD(Time_Duration, __unserialize)
 		goto fail;
 	}

+	if ((Z_TYPE_P(negative) == IS_TRUE) != duration->duration.negative) {
+		goto fail;
+	}
+
 	return;

  fail: