diff --git a/NEWS b/NEWS index bf80ab696b09..1984a4a81dc5 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,8 @@ PHP NEWS (timwolla) . Fixed double-free when assigning to $errors fails when using the Uri\WhatWg\Url parser. (timwolla) + . Reject out-of-range ports when using the Uri\Rfc3986\Uri parser. + (timwolla) . Clean up naming of internal API. (timwolla) 28 Aug 2025, PHP 8.5.0beta2 diff --git a/ext/uri/tests/058.phpt b/ext/uri/tests/058.phpt new file mode 100644 index 000000000000..efca0a0e8aaf --- /dev/null +++ b/ext/uri/tests/058.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test that integer overflows in the port are rejected +--EXTENSIONS-- +uri +--FILE-- +getPort(), PHP_EOL; + echo "2147483647", PHP_EOL; +} else { + $uri = new \Uri\Rfc3986\Uri('https://example.com:2147483647'); + echo "9223372036854775807", PHP_EOL; + echo $uri->getPort(), PHP_EOL; +} + +try { + if (PHP_INT_SIZE == 8) { + new \Uri\Rfc3986\Uri('https://example.com:9223372036854775808'); + } else { + new \Uri\Rfc3986\Uri('https://example.com:2147483648'); + } +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +9223372036854775807 +2147483647 +Uri\InvalidUriException: The port is out of range diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index cf7235b071b4..f9581e6d93ed 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -190,15 +190,22 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(const return SUCCESS; } -ZEND_ATTRIBUTE_NONNULL static size_t str_to_int(const char *str, size_t len) +ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char *str, size_t len) { - size_t result = 0; + if (len > MAX_LENGTH_OF_LONG) { + return -1; + } + + char buf[MAX_LENGTH_OF_LONG + 1]; + *(char*)zend_mempcpy(buf, str, len) = 0; + + zend_ulong result = ZEND_STRTOUL(buf, NULL, 10); - for (size_t i = 0; i < len; ++i) { - result = result * 10 + (str[i] - '0'); + if (result > ZEND_LONG_MAX) { + return -1; } - return result; + return (zend_long)result; } ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) @@ -206,7 +213,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(const const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); if (has_text_range(&uriparser_uri->portText)) { - ZVAL_LONG(retval, str_to_int(uriparser_uri->portText.first, get_text_range_length(&uriparser_uri->portText))); + ZVAL_LONG(retval, port_str_to_zend_long_checked(uriparser_uri->portText.first, get_text_range_length(&uriparser_uri->portText))); } else { ZVAL_NULL(retval); } @@ -319,6 +326,17 @@ php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str /* Make the resulting URI independent of the 'uri_str'. */ uriMakeOwnerMmA(&uri, mm); + if ( + has_text_range(&uri.portText) + && port_str_to_zend_long_checked(uri.portText.first, get_text_range_length(&uri.portText)) == -1 + ) { + if (!silent) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The port is out of range", 0); + } + + goto fail; + } + php_uri_parser_rfc3986_uris *uriparser_uris = uriparser_create_uris(); uriparser_uris->uri = uri;