Skip to content

Commit f1e6ac1

Browse files
committed
ext/standard: Improve handling of timeout values
1 parent f0e1847 commit f1e6ac1

File tree

1 file changed

+57
-45
lines changed

1 file changed

+57
-45
lines changed

ext/standard/streamsfuncs.c

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ PHP_FUNCTION(stream_socket_client)
9999
zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
100100
double timeout;
101101
bool timeout_is_null = 1;
102-
php_timeout_ull conv;
103102
struct timeval tv;
104103
char *hashkey = NULL;
105104
php_stream *stream = NULL;
@@ -122,33 +121,38 @@ PHP_FUNCTION(stream_socket_client)
122121

123122
if (timeout_is_null) {
124123
timeout = (double)FG(default_socket_timeout);
125-
} else if (!zend_finite(timeout)) {
124+
} else if (!zend_finite(timeout) || zend_isnan(timeout)) {
126125
zend_argument_value_error(4, "must be a finite value");
127126
RETURN_THROWS();
128127
}
129128

130129
context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
131130

132-
if (flags & PHP_STREAM_CLIENT_PERSISTENT) {
133-
spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(host));
134-
}
135-
136131
/* prepare the timeout value for use */
137132
struct timeval *tv_pointer;
138-
if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) {
133+
if (timeout < 0.0) {
134+
php_error_docref(NULL, E_WARNING, "timeout must greater than or equal to 0");
135+
if (UNEXPECTED(EG(exception))) {
136+
RETURN_THROWS();
137+
}
139138
tv_pointer = NULL;
140139
} else {
141-
conv = (php_timeout_ull) (timeout * 1000000.0);
142-
#ifdef PHP_WIN32
143-
tv.tv_sec = (long)(conv / 1000000);
144-
tv.tv_usec = (long)(conv % 1000000);
145-
#else
146-
tv.tv_sec = conv / 1000000;
147-
tv.tv_usec = conv % 1000000;
148-
#endif
140+
double microseconds_f = 0;
141+
double seconds = 0;
142+
143+
seconds = modf(timeout, &microseconds_f);
144+
microseconds_f *= 1000000.0;
145+
long microseconds = lround(microseconds_f);
146+
147+
tv.tv_sec = (time_t) seconds;
148+
tv.tv_usec = microseconds;
149149
tv_pointer = &tv;
150150
}
151151

152+
if (flags & PHP_STREAM_CLIENT_PERSISTENT) {
153+
spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(host));
154+
}
155+
152156
if (zerrno) {
153157
ZEND_TRY_ASSIGN_REF_LONG(zerrno, 0);
154158
}
@@ -262,7 +266,6 @@ PHP_FUNCTION(stream_socket_accept)
262266
bool timeout_is_null = 1;
263267
zval *zpeername = NULL;
264268
zend_string *peername = NULL;
265-
php_timeout_ull conv;
266269
struct timeval tv;
267270
php_stream *stream = NULL, *clistream = NULL;
268271
zval *zstream;
@@ -286,17 +289,22 @@ PHP_FUNCTION(stream_socket_accept)
286289

287290
/* prepare the timeout value for use */
288291
struct timeval *tv_pointer;
289-
if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) {
292+
if (timeout < 0.0) {
293+
php_error_docref(NULL, E_WARNING, "timeout must greater than or equal to 0");
294+
if (UNEXPECTED(EG(exception))) {
295+
RETURN_THROWS();
296+
}
290297
tv_pointer = NULL;
291298
} else {
292-
conv = (php_timeout_ull) (timeout * 1000000.0);
293-
#ifdef PHP_WIN32
294-
tv.tv_sec = (long)(conv / 1000000);
295-
tv.tv_usec = (long)(conv % 1000000);
296-
#else
297-
tv.tv_sec = conv / 1000000;
298-
tv.tv_usec = conv % 1000000;
299-
#endif
299+
double microseconds_f = 0;
300+
double seconds = 0;
301+
302+
seconds = modf(timeout, &microseconds_f);
303+
microseconds_f *= 1000000.0;
304+
long microseconds = lround(microseconds_f);
305+
306+
tv.tv_sec = (time_t) seconds;
307+
tv.tv_usec = microseconds;
300308
tv_pointer = &tv;
301309
}
302310

@@ -830,11 +838,16 @@ PHP_FUNCTION(stream_select)
830838
} else if (usec < 0) {
831839
zend_argument_value_error(5, "must be greater than or equal to 0");
832840
RETURN_THROWS();
841+
} else if (usec >= 1000000) {
842+
php_error_docref(NULL, E_WARNING, "must be less than 1000000");
843+
if (UNEXPECTED(EG(exception))) {
844+
RETURN_THROWS();
845+
}
833846
}
834847

835848
/* Windows, Solaris and BSD do not like microsecond values which are >= 1 sec */
836-
tv.tv_sec = (long)(sec + (usec / 1000000));
837-
tv.tv_usec = (long)(usec % 1000000);
849+
tv.tv_sec = (time_t)(sec + (usec / 1000000));
850+
tv.tv_usec = (suseconds_t)(usec % 1000000);
838851
tv_p = &tv;
839852
}
840853

@@ -1419,7 +1432,6 @@ PHP_FUNCTION(stream_set_timeout)
14191432
zend_long seconds, microseconds = 0;
14201433
struct timeval t;
14211434
php_stream *stream;
1422-
int argc = ZEND_NUM_ARGS();
14231435

14241436
ZEND_PARSE_PARAMETERS_START(2, 3)
14251437
Z_PARAM_RESOURCE(socket)
@@ -1430,25 +1442,25 @@ PHP_FUNCTION(stream_set_timeout)
14301442

14311443
php_stream_from_zval(stream, socket);
14321444

1433-
#ifdef PHP_WIN32
1434-
t.tv_sec = (long)seconds;
1435-
1436-
if (argc == 3) {
1437-
t.tv_usec = (long)(microseconds % 1000000);
1438-
t.tv_sec +=(long)(microseconds / 1000000);
1439-
} else {
1440-
t.tv_usec = 0;
1445+
if (seconds < 0) {
1446+
php_error_docref(NULL, E_WARNING, "must be greater than or equal to 0");
1447+
if (UNEXPECTED(EG(exception))) {
1448+
RETURN_THROWS();
1449+
}
14411450
}
1442-
#else
1443-
t.tv_sec = seconds;
1444-
1445-
if (argc == 3) {
1446-
t.tv_usec = microseconds % 1000000;
1447-
t.tv_sec += microseconds / 1000000;
1448-
} else {
1449-
t.tv_usec = 0;
1451+
if (microseconds < 0 || microseconds >= 1000000) {
1452+
php_error_docref(NULL, E_WARNING, "must be between 0 and 999999");
1453+
if (UNEXPECTED(EG(exception))) {
1454+
RETURN_THROWS();
1455+
}
1456+
if (microseconds >= 1000000) {
1457+
seconds += microseconds / 1000000;
1458+
microseconds %= 1000000;
1459+
}
14501460
}
1451-
#endif
1461+
1462+
t.tv_sec = (time_t) seconds;
1463+
t.tv_usec = (suseconds_t) microseconds;
14521464

14531465
if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
14541466
RETURN_TRUE;

0 commit comments

Comments
 (0)