Skip to content

Commit 1c9242e

Browse files
committed
Merge branch 'PHP-8.6'
* PHP-8.6: date: Validate ISO 8601 duration strings for null bytes early (#24357) date: Validate nanoseconds range and reject negative zero duration (#24356)
2 parents 6a02262 + 90a271f commit 1c9242e

3 files changed

Lines changed: 41 additions & 2 deletions

File tree

‎ext/date/tests/time/duration/methods/fromIso8601DurationString.phpt‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ $specifications = [
3434
'P1DT0S',
3535
'2000-01-01T00:00:00Z',
3636
'2000-01-01T00:00:00Z/PT1H',
37+
38+
"PT5S\0garbage",
39+
"PT5S\0",
40+
"\0PT5S",
3741
];
3842

3943
foreach ($specifications as $specification) {
40-
printf("%-25s: ", $specification);
44+
printf("%-25s: ", addcslashes($specification, "\0"));
4145

4246
try {
4347
echo f(Time\Duration::fromIso8601DurationString($specification)), PHP_EOL;
@@ -72,3 +76,6 @@ P1M : Time\TimeException: The ISO 8601 duration string may
7276
P1DT0S : Time\TimeException: The ISO 8601 duration string may only contain the time (T) aspect
7377
2000-01-01T00:00:00Z : Time\TimeException: The ISO 8601 duration string is missing the period (P) aspect
7478
2000-01-01T00:00:00Z/PT1H: Time\TimeException: The ISO 8601 duration string may only contain the period (P) aspect
79+
PT5S\000garbage : Time\TimeException: The ISO 8601 duration string could not be parsed
80+
PT5S\000 : Time\TimeException: The ISO 8601 duration string could not be parsed
81+
\000PT5S : Time\TimeException: The ISO 8601 duration string could not be parsed

‎ext/date/tests/time/duration/serialize.phpt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ try {
4646
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
4747
}
4848

49+
try {
50+
// Nanoseconds that would be truncated to a valid value by a 32-bit conversion (2^32 + 5).
51+
unserialize('O:13:"Time\Duration":3:{s:7:"seconds";i:1;s:11:"nanoseconds";i:4294967301;s:8:"negative";b:0;}');
52+
} catch (Throwable $e) {
53+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
54+
}
55+
56+
try {
57+
// A negative zero duration is rejected.
58+
unserialize('O:13:"Time\Duration":3:{s:7:"seconds";i:0;s:11:"nanoseconds";i:0;s:8:"negative";b:1;}');
59+
} catch (Throwable $e) {
60+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
61+
}
62+
4963
try {
5064
Time\Duration::fromSeconds(1, 1)
5165
->__unserialize([
@@ -69,3 +83,5 @@ Exception: Invalid serialization data for Time\Duration object
6983
Exception: Invalid serialization data for Time\Duration object
7084
Exception: Invalid serialization data for Time\Duration object
7185
Exception: Invalid serialization data for Time\Duration object
86+
Exception: Invalid serialization data for Time\Duration object
87+
Exception: Invalid serialization data for Time\Duration object

‎ext/date/time_duration.c‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,18 @@ PHP_METHOD(Time_Duration, __unserialize)
175175
goto fail;
176176
}
177177

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

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

201+
if ((Z_TYPE_P(negative) == IS_TRUE) != duration->duration.negative) {
202+
goto fail;
203+
}
204+
194205
return;
195206

196207
fail:
@@ -315,6 +326,11 @@ PHP_METHOD(Time_Duration, fromIso8601DurationString)
315326
Z_PARAM_STR(specification);
316327
ZEND_PARSE_PARAMETERS_END();
317328

329+
if (zend_str_has_nul_byte(specification)) {
330+
throw_timelib_error(TIMELIB_ERROR_ISO8601_DURATION_PARSE_FAILURE);
331+
RETURN_THROWS();
332+
}
333+
318334
int error;
319335
timelib_duration *d = timelib_duration_create_from_iso8601string(ZSTR_VAL(specification), &error);
320336
if (error != TIMELIB_ERROR_NO_ERROR) {

0 commit comments

Comments
 (0)