From c4bb6e6c393f235b5d07df95c063e8a47d97cd6d Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Fri, 20 Dec 2024 22:36:14 +0000 Subject: [PATCH] ext/sockets: further timeout handling changes. (#17210) close GH-17210 --- NEWS | 2 ++ ext/sockets/sockets.c | 35 +++++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 23980a36ca7..1d18cbd732f 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,8 @@ PHP NEWS (David Carlier) . Added TCP_FUNCTION_BLK to change the TCP stack algorithm on FreeBSD. (David Carlier) + . socket_set_option() catches possible overflow with SO_RCVTIMEO/SO_SNDTIMEO + with timeout setting on windows. (David Carlier) - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index abd103823da..31250d02262 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1646,6 +1646,8 @@ PHP_FUNCTION(socket_get_option) struct timeval tv; #ifdef PHP_WIN32 DWORD timeout = 0; +#else + struct timeval timeout; #endif socklen_t optlen; php_socket *php_sock; @@ -1749,14 +1751,6 @@ PHP_FUNCTION(socket_get_option) case SO_RCVTIMEO: case SO_SNDTIMEO: -#ifndef PHP_WIN32 - optlen = sizeof(tv); - - if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&tv, &optlen) != 0) { - PHP_SOCKET_ERROR(php_sock, "Unable to retrieve socket option", errno); - RETURN_FALSE; - } -#else optlen = sizeof(timeout); if (getsockopt(php_sock->bsd_socket, level, optname, (char*)&timeout, &optlen) != 0) { @@ -1764,8 +1758,12 @@ PHP_FUNCTION(socket_get_option) RETURN_FALSE; } +#ifndef PHP_WIN32 + tv.tv_sec = timeout.tv_sec; + tv.tv_usec = timeout.tv_usec; +#else tv.tv_sec = timeout ? (long)(timeout / 1000) : 0; - tv.tv_usec = timeout ? (long)((timeout * 1000) % 1000000) : 0; + tv.tv_usec = timeout ? (long)((timeout % 1000) * 1000) : 0; #endif array_init(return_value); @@ -2046,7 +2044,24 @@ PHP_FUNCTION(socket_set_option) optlen = sizeof(tv); opt_ptr = &tv; #else - timeout = Z_LVAL_P(sec) * 1000 + Z_LVAL_P(usec) / 1000; + if (valsec < 0 || valsec > ULONG_MAX / 1000) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %u", sec_key, (ULONG_MAX / 1000)); + RETURN_THROWS(); + } + + timeout = valsec * 1000; + + + /* + * We deliberately throw if (valusec / 1000) > ULONG_MAX, treating it as a programmer error. + * On Windows, ULONG_MAX = 2^32, unlike ZEND_LONG_MAX = 2^63. + */ + if (valusec < 0 || timeout > ULONG_MAX - (valusec / 1000)) { + zend_argument_value_error(4, "\"%s\" must be between 0 and %u", usec_key, (DWORD)(ULONG_MAX - (valusec / 1000))); + RETURN_THROWS(); + } + + timeout += valusec / 1000; optlen = sizeof(timeout); opt_ptr = &timeout; #endif