Skip to content

Commit e242469

Browse files
committed
uri: Reject ports overflowing zend_long for uri_parser_rfc3986
RFC 3986 technically allows arbitrarily large integers as port numbers, but our implementation is unable to deal with that, since it expects the port to fit `zend_long`, reject those explicitly instead of misinterpreting them.
1 parent 243aedd commit e242469

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ PHP NEWS
2525
(timwolla)
2626
. Fixed double-free when assigning to $errors fails when using
2727
the Uri\WhatWg\Url parser. (timwolla)
28+
. Reject out-of-range ports when using the Uri\Rfc3986\Uri parser.
29+
(timwolla)
2830
. Clean up naming of internal API. (timwolla)
2931

3032
28 Aug 2025, PHP 8.5.0beta2

ext/uri/tests/058.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Test that integer overflows in the port are rejected
3+
--EXTENSIONS--
4+
uri
5+
--FILE--
6+
<?php
7+
8+
if (PHP_INT_SIZE == 8) {
9+
$uri = new \Uri\Rfc3986\Uri('https://example.com:9223372036854775807');
10+
echo $uri->getPort(), PHP_EOL;
11+
echo "2147483647", PHP_EOL;
12+
} else {
13+
$uri = new \Uri\Rfc3986\Uri('https://example.com:2147483647');
14+
echo "9223372036854775807", PHP_EOL;
15+
echo $uri->getPort(), PHP_EOL;
16+
}
17+
18+
try {
19+
if (PHP_INT_SIZE == 8) {
20+
new \Uri\Rfc3986\Uri('https://example.com:9223372036854775808');
21+
} else {
22+
new \Uri\Rfc3986\Uri('https://example.com:2147483648');
23+
}
24+
} catch (Throwable $e) {
25+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
26+
}
27+
28+
?>
29+
--EXPECT--
30+
9223372036854775807
31+
2147483647
32+
Uri\InvalidUriException: The port is out of range

ext/uri/uri_parser_rfc3986.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,30 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(const
190190
return SUCCESS;
191191
}
192192

193-
ZEND_ATTRIBUTE_NONNULL static size_t str_to_int(const char *str, size_t len)
193+
ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char *str, size_t len)
194194
{
195-
size_t result = 0;
195+
if (len > MAX_LENGTH_OF_LONG) {
196+
return -1;
197+
}
198+
199+
char buf[MAX_LENGTH_OF_LONG + 1];
200+
*(char*)zend_mempcpy(buf, str, len) = 0;
201+
202+
zend_ulong result = ZEND_STRTOUL(buf, NULL, 10);
196203

197-
for (size_t i = 0; i < len; ++i) {
198-
result = result * 10 + (str[i] - '0');
204+
if (result > ZEND_LONG_MAX) {
205+
return -1;
199206
}
200207

201-
return result;
208+
return (zend_long)result;
202209
}
203210

204211
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)
205212
{
206213
const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode);
207214

208215
if (has_text_range(&uriparser_uri->portText)) {
209-
ZVAL_LONG(retval, str_to_int(uriparser_uri->portText.first, get_text_range_length(&uriparser_uri->portText)));
216+
ZVAL_LONG(retval, port_str_to_zend_long_checked(uriparser_uri->portText.first, get_text_range_length(&uriparser_uri->portText)));
210217
} else {
211218
ZVAL_NULL(retval);
212219
}
@@ -319,6 +326,17 @@ php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str
319326
/* Make the resulting URI independent of the 'uri_str'. */
320327
uriMakeOwnerMmA(&uri, mm);
321328

329+
if (
330+
has_text_range(&uri.portText)
331+
&& port_str_to_zend_long_checked(uri.portText.first, get_text_range_length(&uri.portText)) == -1
332+
) {
333+
if (!silent) {
334+
zend_throw_exception(uri_invalid_uri_exception_ce, "The port is out of range", 0);
335+
}
336+
337+
goto fail;
338+
}
339+
322340
php_uri_parser_rfc3986_uris *uriparser_uris = uriparser_create_uris();
323341
uriparser_uris->uri = uri;
324342

0 commit comments

Comments
 (0)