Skip to content

Commit 063393f

Browse files
committed
- Fixed bug #53592 (stream_socket_enable_crypto() busy-waits in client mode).
- Fixed stream_socket_enable_crypto() not honoring the socket timeout in server mode.
1 parent 1d7294f commit 063393f

File tree

1 file changed

+54
-18
lines changed

1 file changed

+54
-18
lines changed

ext/openssl/xp_ssl.c

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,10 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
411411
int n, retry = 1;
412412

413413
if (cparam->inputs.activate && !sslsock->ssl_active) {
414-
float timeout = sslsock->connect_timeout.tv_sec + sslsock->connect_timeout.tv_usec / 1000000;
415-
int blocked = sslsock->s.is_blocked;
414+
struct timeval start_time,
415+
*timeout;
416+
int blocked = sslsock->s.is_blocked,
417+
has_timeout = 0;
416418

417419
#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
418420
if (sslsock->is_client && sslsock->sni) {
@@ -429,36 +431,70 @@ static inline int php_openssl_enable_crypto(php_stream *stream,
429431
sslsock->state_set = 1;
430432
}
431433

432-
if (sslsock->is_client && SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
433-
sslsock->s.is_blocked = 0;
434+
if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
435+
sslsock->s.is_blocked = 0;
434436
}
437+
438+
timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
439+
has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
440+
/* gettimeofday is not monotonic; using it here is not strictly correct */
441+
if (has_timeout) {
442+
gettimeofday(&start_time, NULL);
443+
}
444+
435445
do {
446+
struct timeval cur_time,
447+
elapsed_time;
448+
436449
if (sslsock->is_client) {
437-
struct timeval tvs, tve;
438-
struct timezone tz;
439-
440-
gettimeofday(&tvs, &tz);
441450
n = SSL_connect(sslsock->ssl_handle);
442-
gettimeofday(&tve, &tz);
451+
} else {
452+
n = SSL_accept(sslsock->ssl_handle);
453+
}
443454

444-
timeout -= (tve.tv_sec + (float) tve.tv_usec / 1000000) - (tvs.tv_sec + (float) tvs.tv_usec / 1000000);
445-
if (timeout < 0) {
446-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: connection timeout");
455+
if (has_timeout) {
456+
gettimeofday(&cur_time, NULL);
457+
elapsed_time.tv_sec = cur_time.tv_sec - start_time.tv_sec;
458+
elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
459+
if (cur_time.tv_usec < start_time.tv_usec) {
460+
elapsed_time.tv_sec -= 1L;
461+
elapsed_time.tv_usec += 1000000L;
462+
}
463+
464+
if (elapsed_time.tv_sec > timeout->tv_sec ||
465+
(elapsed_time.tv_sec == timeout->tv_sec &&
466+
elapsed_time.tv_usec > timeout->tv_usec)) {
467+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout");
447468
return -1;
448469
}
449-
} else {
450-
n = SSL_accept(sslsock->ssl_handle);
451470
}
452471

453472
if (n <= 0) {
454-
retry = handle_ssl_error(stream, n, sslsock->is_client || sslsock->s.is_blocked TSRMLS_CC);
455-
473+
/* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */
474+
retry = handle_ssl_error(stream, n, blocked TSRMLS_CC);
475+
if (retry) {
476+
/* wait until something interesting happens in the socket. It may be a
477+
* timeout. Also consider the unlikely of possibility of a write block */
478+
int err = SSL_get_error(sslsock->ssl_handle, n);
479+
struct timeval left_time;
480+
481+
if (has_timeout) {
482+
left_time.tv_sec = timeout->tv_sec - elapsed_time.tv_sec;
483+
left_time.tv_usec = timeout->tv_usec - elapsed_time.tv_usec;
484+
if (timeout->tv_usec < elapsed_time.tv_usec) {
485+
left_time.tv_sec -= 1L;
486+
left_time.tv_usec += 1000000L;
487+
}
488+
}
489+
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
490+
(POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
491+
}
456492
} else {
457-
break;
493+
retry = 0;
458494
}
459495
} while (retry);
460496

461-
if (sslsock->is_client && sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
497+
if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
462498
sslsock->s.is_blocked = blocked;
463499
}
464500

0 commit comments

Comments
 (0)