Skip to content

Commit 9f586f6

Browse files
authored
Fixed int32 underflow on DateTime::createFromTimestamp (#12775)
* Fixed int32 underflow on DateTime::createFromTimestamp
1 parent 868f1a3 commit 9f586f6

File tree

4 files changed

+170
-76
lines changed

4 files changed

+170
-76
lines changed

ext/date/php_date.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,16 +2525,14 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts)
25252525
zend_long sec;
25262526
int usec;
25272527

2528-
if (UNEXPECTED(isnan(sec_dval)
2529-
|| sec_dval >= (double)TIMELIB_LONG_MAX
2530-
|| sec_dval < (double)TIMELIB_LONG_MIN
2531-
)) {
2532-
zend_throw_error(
2528+
if (UNEXPECTED(isnan(sec_dval) || !PHP_DATE_DOUBLE_FITS_LONG(sec_dval))) {
2529+
zend_argument_error(
25332530
date_ce_date_range_error,
2534-
"Seconds must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ", %g given",
2531+
1,
2532+
"must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ".999999, %g given",
25352533
TIMELIB_LONG_MIN,
25362534
TIMELIB_LONG_MAX,
2537-
sec_dval
2535+
ts
25382536
);
25392537
return false;
25402538
}
@@ -2543,6 +2541,18 @@ PHPAPI bool php_date_initialize_from_ts_double(php_date_obj *dateobj, double ts)
25432541
usec = (int)(fmod(ts, 1) * 1000000);
25442542

25452543
if (UNEXPECTED(usec < 0)) {
2544+
if (UNEXPECTED(sec == TIMELIB_LONG_MIN)) {
2545+
zend_argument_error(
2546+
date_ce_date_range_error,
2547+
1,
2548+
"must be a finite number between " TIMELIB_LONG_FMT " and " TIMELIB_LONG_FMT ".999999, %g given",
2549+
TIMELIB_LONG_MIN,
2550+
TIMELIB_LONG_MAX,
2551+
ts
2552+
);
2553+
return false;
2554+
}
2555+
25462556
sec = sec - 1;
25472557
usec = 1000000 + usec;
25482558
}

ext/date/php_date.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@
2020
#include "lib/timelib.h"
2121
#include "Zend/zend_hash.h"
2222

23+
/* Same as SIZEOF_ZEND_LONG but using TIMELIB_LONG_MAX/MIN */
24+
#if TIMELIB_LONG_MAX == INT32_MAX
25+
# define PHP_DATE_SIZEOF_LONG 4
26+
#elif TIMELIB_LONG_MAX == INT64_MAX
27+
# define PHP_DATE_SIZEOF_LONG 8
28+
#else
29+
# error "Unknown TIMELIB LONG SIZE"
30+
#endif
31+
32+
/* Same as ZEND_DOUBLE_FITS_LONG but using TIMELIB_LONG_MAX/MIN */
33+
#if PHP_DATE_SIZEOF_LONG == 4
34+
# define PHP_DATE_DOUBLE_FITS_LONG(d) (!((d) > (double)TIMELIB_LONG_MAX || (d) < (double)TIMELIB_LONG_MIN))
35+
#elif PHP_DATE_SIZEOF_LONG == 8
36+
/* >= as (double)TIMELIB_LONG_MAX is outside signed range */
37+
# define PHP_DATE_DOUBLE_FITS_LONG(d) (!((d) >= (double)TIMELIB_LONG_MAX || (d) < (double)TIMELIB_LONG_MIN))
38+
#endif
39+
2340
#include "php_version.h"
2441
#define PHP_DATE_VERSION PHP_VERSION
2542

ext/date/tests/createFromTimestamp.phpt

Lines changed: 10 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
--TEST--
22
Tests for DateTime[Immutable]::createFromTimestamp
3-
--SKIPIF--
4-
<?php
5-
if (PHP_INT_SIZE === 4) die('xfail fails on 32-bit');
6-
?>
73
--INI--
84
date.timezone=Europe/London
95
--FILE--
@@ -12,10 +8,6 @@ date.timezone=Europe/London
128
class MyDateTime extends DateTime {};
139
class MyDateTimeImmutable extends DateTimeImmutable {};
1410

15-
define('MAX_32BIT', 2147483647);
16-
// -2147483648 may not be expressed in a literal due to parsing peculiarities.
17-
define('MIN_32BIT', -2147483647 - 1);
18-
1911
$timestamps = array(
2012
1696883232,
2113
-1696883232,
@@ -26,9 +18,6 @@ $timestamps = array(
2618
0,
2719
0.0,
2820
-0.0,
29-
MAX_32BIT,
30-
MIN_32BIT,
31-
MIN_32BIT - 0.5,
3221
PHP_INT_MAX + 1024.0,
3322
PHP_INT_MIN - 1025.0,
3423
NAN,
@@ -212,64 +201,16 @@ DateTimeImmutable::createFromTimestamp(-0.0): object(DateTimeImmutable)#%d (3) {
212201
["timezone"]=>
213202
string(6) "+00:00"
214203
}
215-
DateTime::createFromTimestamp(2147483647): object(DateTime)#%d (3) {
216-
["date"]=>
217-
string(26) "2038-01-19 03:14:07.000000"
218-
["timezone_type"]=>
219-
int(1)
220-
["timezone"]=>
221-
string(6) "+00:00"
222-
}
223-
DateTimeImmutable::createFromTimestamp(2147483647): object(DateTimeImmutable)#%d (3) {
224-
["date"]=>
225-
string(26) "2038-01-19 03:14:07.000000"
226-
["timezone_type"]=>
227-
int(1)
228-
["timezone"]=>
229-
string(6) "+00:00"
230-
}
231-
DateTime::createFromTimestamp(-2147483648): object(DateTime)#%d (3) {
232-
["date"]=>
233-
string(26) "1901-12-13 20:45:52.000000"
234-
["timezone_type"]=>
235-
int(1)
236-
["timezone"]=>
237-
string(6) "+00:00"
238-
}
239-
DateTimeImmutable::createFromTimestamp(-2147483648): object(DateTimeImmutable)#%d (3) {
240-
["date"]=>
241-
string(26) "1901-12-13 20:45:52.000000"
242-
["timezone_type"]=>
243-
int(1)
244-
["timezone"]=>
245-
string(6) "+00:00"
246-
}
247-
DateTime::createFromTimestamp(-2147483648.5): object(DateTime)#%d (3) {
248-
["date"]=>
249-
string(26) "1901-12-13 20:45:51.500000"
250-
["timezone_type"]=>
251-
int(1)
252-
["timezone"]=>
253-
string(6) "+00:00"
254-
}
255-
DateTimeImmutable::createFromTimestamp(-2147483648.5): object(DateTimeImmutable)#%d (3) {
256-
["date"]=>
257-
string(26) "1901-12-13 20:45:51.500000"
258-
["timezone_type"]=>
259-
int(1)
260-
["timezone"]=>
261-
string(6) "+00:00"
262-
}
263-
DateTime::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given
264-
DateTimeImmutable::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given
265-
DateTime::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given
266-
DateTimeImmutable::createFromTimestamp(%f): DateRangeError: Seconds must be a finite number between %i and %i, %f given
267-
DateTime::createFromTimestamp(NAN): DateRangeError: Seconds must be a finite number between %i and %i, NAN given
268-
DateTimeImmutable::createFromTimestamp(NAN): DateRangeError: Seconds must be a finite number between %i and %i, NAN given
269-
DateTime::createFromTimestamp(INF): DateRangeError: Seconds must be a finite number between %i and %i, INF given
270-
DateTimeImmutable::createFromTimestamp(INF): DateRangeError: Seconds must be a finite number between %i and %i, INF given
271-
DateTime::createFromTimestamp(-INF): DateRangeError: Seconds must be a finite number between %i and %i, -INF given
272-
DateTimeImmutable::createFromTimestamp(-INF): DateRangeError: Seconds must be a finite number between %i and %i, -INF given
204+
DateTime::createFromTimestamp(%f): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given
205+
DateTimeImmutable::createFromTimestamp(%f): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given
206+
DateTime::createFromTimestamp(%f): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given
207+
DateTimeImmutable::createFromTimestamp(%f): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, %f given
208+
DateTime::createFromTimestamp(NAN): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, NAN given
209+
DateTimeImmutable::createFromTimestamp(NAN): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, NAN given
210+
DateTime::createFromTimestamp(INF): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, INF given
211+
DateTimeImmutable::createFromTimestamp(INF): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, INF given
212+
DateTime::createFromTimestamp(-INF): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, -INF given
213+
DateTimeImmutable::createFromTimestamp(-INF): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between %i and %i.999999, -INF given
273214
MyDateTime::createFromTimestamp(0): object(MyDateTime)#%d (3) {
274215
["date"]=>
275216
string(26) "1970-01-01 00:00:00.000000"
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
--TEST--
2+
Tests for DateTime[Immutable]::createFromTimestamp 32bit variant
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only");
6+
?>
7+
--INI--
8+
date.timezone=Europe/London
9+
--FILE--
10+
<?php
11+
12+
$timestamps = array(
13+
PHP_INT_MAX,
14+
PHP_INT_MIN,
15+
PHP_INT_MAX + 0.5,
16+
PHP_INT_MIN + 0.5,
17+
PHP_INT_MAX - 0.5,
18+
PHP_INT_MIN - 0.5,
19+
PHP_INT_MAX + 1024.0,
20+
PHP_INT_MIN - 1025.0,
21+
);
22+
23+
foreach ($timestamps as $ts) {
24+
echo 'DateTime::createFromTimestamp(' . var_export($ts, true) . '): ';
25+
try {
26+
var_dump(DateTime::createFromTimestamp($ts));
27+
} catch (Throwable $e) {
28+
echo get_class($e) . ': ' . $e->getMessage() . "\n";
29+
}
30+
31+
echo 'DateTimeImmutable::createFromTimestamp(' . var_export($ts, true) . '): ';
32+
try {
33+
var_dump(DateTimeImmutable::createFromTimestamp($ts));
34+
} catch (Throwable $e) {
35+
echo get_class($e) . ': ' . $e->getMessage() . "\n";
36+
}
37+
}
38+
39+
?>
40+
--EXPECTF--
41+
DateTime::createFromTimestamp(2147483647): object(DateTime)#%d (3) {
42+
["date"]=>
43+
string(26) "2038-01-19 03:14:07.000000"
44+
["timezone_type"]=>
45+
int(1)
46+
["timezone"]=>
47+
string(6) "+00:00"
48+
}
49+
DateTimeImmutable::createFromTimestamp(2147483647): object(DateTimeImmutable)#%d (3) {
50+
["date"]=>
51+
string(26) "2038-01-19 03:14:07.000000"
52+
["timezone_type"]=>
53+
int(1)
54+
["timezone"]=>
55+
string(6) "+00:00"
56+
}
57+
DateTime::createFromTimestamp(-2147483647-1): object(DateTime)#%d (3) {
58+
["date"]=>
59+
string(26) "1901-12-13 20:45:52.000000"
60+
["timezone_type"]=>
61+
int(1)
62+
["timezone"]=>
63+
string(6) "+00:00"
64+
}
65+
DateTimeImmutable::createFromTimestamp(-2147483647-1): object(DateTimeImmutable)#%d (3) {
66+
["date"]=>
67+
string(26) "1901-12-13 20:45:52.000000"
68+
["timezone_type"]=>
69+
int(1)
70+
["timezone"]=>
71+
string(6) "+00:00"
72+
}
73+
DateTime::createFromTimestamp(2147483647.5): object(DateTime)#%d (3) {
74+
["date"]=>
75+
string(26) "2038-01-19 03:14:07.500000"
76+
["timezone_type"]=>
77+
int(1)
78+
["timezone"]=>
79+
string(6) "+00:00"
80+
}
81+
DateTimeImmutable::createFromTimestamp(2147483647.5): object(DateTimeImmutable)#%d (3) {
82+
["date"]=>
83+
string(26) "2038-01-19 03:14:07.500000"
84+
["timezone_type"]=>
85+
int(1)
86+
["timezone"]=>
87+
string(6) "+00:00"
88+
}
89+
DateTime::createFromTimestamp(-2147483647.5): object(DateTime)#%d (3) {
90+
["date"]=>
91+
string(26) "1901-12-13 20:45:52.500000"
92+
["timezone_type"]=>
93+
int(1)
94+
["timezone"]=>
95+
string(6) "+00:00"
96+
}
97+
DateTimeImmutable::createFromTimestamp(-2147483647.5): object(DateTimeImmutable)#%d (3) {
98+
["date"]=>
99+
string(26) "1901-12-13 20:45:52.500000"
100+
["timezone_type"]=>
101+
int(1)
102+
["timezone"]=>
103+
string(6) "+00:00"
104+
}
105+
DateTime::createFromTimestamp(2147483646.5): object(DateTime)#%d (3) {
106+
["date"]=>
107+
string(26) "2038-01-19 03:14:06.500000"
108+
["timezone_type"]=>
109+
int(1)
110+
["timezone"]=>
111+
string(6) "+00:00"
112+
}
113+
DateTimeImmutable::createFromTimestamp(2147483646.5): object(DateTimeImmutable)#%d (3) {
114+
["date"]=>
115+
string(26) "2038-01-19 03:14:06.500000"
116+
["timezone_type"]=>
117+
int(1)
118+
["timezone"]=>
119+
string(6) "+00:00"
120+
}
121+
DateTime::createFromTimestamp(-2147483648.5): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given
122+
DateTimeImmutable::createFromTimestamp(-2147483648.5): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given
123+
DateTime::createFromTimestamp(2147484671.0): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, 2.14748e+9 given
124+
DateTimeImmutable::createFromTimestamp(2147484671.0): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, 2.14748e+9 given
125+
DateTime::createFromTimestamp(-2147484673.0): DateRangeError: DateTime::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given
126+
DateTimeImmutable::createFromTimestamp(-2147484673.0): DateRangeError: DateTimeImmutable::createFromTimestamp(): Argument #1 ($timestamp) must be a finite number between -2147483648 and 2147483647.999999, -2.14748e+9 given

0 commit comments

Comments
 (0)