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: