1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-20366 ext/uri: Do not throw ValueError on null-byte (#20489)

This commit is contained in:
Máté Kocsis
2025-11-19 20:41:27 +01:00
committed by GitHub
parent aa9619e437
commit 9743977f92
24 changed files with 393 additions and 78 deletions

4
NEWS
View File

@@ -61,6 +61,10 @@ PHP NEWS
. Fix assertion failures resulting in crashes with stream filter
object parameters. (ndossche)
- URI:
. Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering
null byte). (kocsismate)
20 Nov 2025, PHP 8.5.0
- Core:

View File

@@ -377,7 +377,7 @@ static void create_rfc3986_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor
zend_object *base_url_object = NULL;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_PATH_STR(uri_str)
Z_PARAM_STR(uri_str)
Z_PARAM_OPTIONAL
Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, php_uri_ce_rfc3986_uri)
ZEND_PARSE_PARAMETERS_END();
@@ -490,7 +490,7 @@ static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor)
zval *errors = NULL;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_PATH_STR(uri_str)
Z_PARAM_STR(uri_str)
Z_PARAM_OPTIONAL
Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, php_uri_ce_whatwg_url)
Z_PARAM_ZVAL(errors)
@@ -553,7 +553,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, withUserInfo)
zend_string *value;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR_OR_NULL(value)
Z_PARAM_STR_OR_NULL(value)
ZEND_PARSE_PARAMETERS_END();
zval zv;
@@ -769,7 +769,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, resolve)
zend_string *uri_str;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR(uri_str)
Z_PARAM_STR(uri_str)
ZEND_PARSE_PARAMETERS_END();
php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU,
@@ -956,7 +956,7 @@ PHP_METHOD(Uri_WhatWg_Url, resolve)
zval *errors = NULL;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_PATH_STR(uri_str)
Z_PARAM_STR(uri_str)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(errors)
ZEND_PARSE_PARAMETERS_END();

View File

@@ -96,7 +96,7 @@ void php_uri_property_write_str_helper(INTERNAL_FUNCTION_PARAMETERS, php_uri_pro
zend_string *value;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR(value)
Z_PARAM_STR(value)
ZEND_PARSE_PARAMETERS_END();
zval zv;
@@ -110,7 +110,7 @@ void php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAMETERS, php
zend_string *value;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH_STR_OR_NULL(value)
Z_PARAM_STR_OR_NULL(value)
ZEND_PARSE_PARAMETERS_END();
zval zv;

View File

@@ -1,39 +0,0 @@
--TEST--
Test URI parsing containing null bytes
--EXTENSIONS--
uri
--FILE--
<?php
try {
new Uri\Rfc3986\Uri("https://exam\0ple.com");
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
$uri = new Uri\Rfc3986\Uri("https://example.com");
try {
$uri->withHost("exam\0ple.com");
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
try {
new Uri\WhatWg\Url("https://exam\0ple.com");
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
$url = new Uri\WhatWg\Url("https://example.com");
try {
$url->withHost("exam\0ple.com");
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
?>
--EXPECT--
Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes
Uri\Rfc3986\Uri::withHost(): Argument #1 ($host) must not contain any null bytes
Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes
Uri\WhatWg\Url::withHost(): Argument #1 ($host) must not contain any null bytes

View File

@@ -0,0 +1,18 @@
--TEST--
Test Uri\Rfc3986\Uri component modification - path - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$uri = Uri\Rfc3986\Uri::parse("https://example.com");
try {
$uri->withPath("/\0foo");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\InvalidUriException: The specified path is malformed

View File

@@ -5,10 +5,10 @@ Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte
try {
new Uri\Rfc3986\Uri("https://exam\0ple.com");
} catch (ValueError $e) {
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
ValueError: Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes
Uri\InvalidUriException: The specified URI is malformed

View File

@@ -1,14 +0,0 @@
--TEST--
Test Uri\Rfc3986\Uri parsing - basic - URI contains null byte
--FILE--
<?php
try {
Uri\Rfc3986\Uri::parse("https://exam\0ple.com");
} catch (ValueError $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
ValueError: Uri\Rfc3986\Uri::parse(): Argument #1 ($uri) must not contain any null bytes

View File

@@ -0,0 +1,18 @@
--TEST--
Test Uri\Rfc3986\Uri reference resolution - resolve() - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$uri = Uri\Rfc3986\Uri::parse("https://example.com");
try {
$uri->resolve("/f\0o");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\InvalidUriException: The specified URI is malformed

View File

@@ -0,0 +1,19 @@
--TEST--
Test Uri\WhatWg\Url component modification - fragment - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->withFragment("frag\0ment");
var_dump($url1->getFragment());
var_dump($url2->getFragment());
var_dump($url2->toAsciiString());
?>
--EXPECT--
NULL
string(11) "frag%00ment"
string(32) "https://example.com/#frag%00ment"

View File

@@ -0,0 +1,18 @@
--TEST--
Test Uri\WhatWg\Url component modification - host - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url = Uri\WhatWg\Url::parse("https://example.com");
try {
$url->withHost("h\0st");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\WhatWg\InvalidUrlException: The specified host is malformed (DomainInvalidCodePoint)

View File

@@ -0,0 +1,20 @@
--TEST--
Test Uri\WhatWg\Url component modification - password - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->withPassword("pass\0word");
var_dump($url1->getPassword());
var_dump($url2->getPassword());
var_dump($url2->toAsciiString());
?>
--EXPECT--
NULL
string(11) "pass%00word"
string(33) "https://:pass%00word@example.com/"

View File

@@ -0,0 +1,19 @@
--TEST--
Test Uri\WhatWg\Url component modification - path - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->withPath("/p\0th\0");
var_dump($url1->getPath());
var_dump($url2->getPath());
var_dump($url2->toAsciiString());
?>
--EXPECT--
string(1) "/"
string(10) "/p%00th%00"
string(29) "https://example.com/p%00th%00"

View File

@@ -0,0 +1,19 @@
--TEST--
Test Uri\WhatWg\Url component modification - query - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->withQuery("f\0o=bar&baz=q\0x");
var_dump($url1->getQuery());
var_dump($url2->getQuery());
var_dump($url2->toAsciiString());
?>
--EXPECT--
NULL
string(19) "f%00o=bar&baz=q%00x"
string(40) "https://example.com/?f%00o=bar&baz=q%00x"

View File

@@ -0,0 +1,18 @@
--TEST--
Test Uri\WhatWg\Url component modification - scheme - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url = Uri\WhatWg\Url::parse("https://example.com");
try {
$url->withScheme("sch\0me");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\WhatWg\InvalidUrlException: The specified scheme is malformed

View File

@@ -0,0 +1,20 @@
--TEST--
Test Uri\WhatWg\Url component modification - username - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->withUsername("usern\0me");
var_dump($url1->getUsername());
var_dump($url2->getUsername());
var_dump($url2->toAsciiString());
?>
--EXPECT--
NULL
string(10) "usern%00me"
string(31) "https://usern%00me@example.com/"

View File

@@ -1,14 +0,0 @@
--TEST--
Test Uri\WhatWg\Url parsing - basic - URL contains null byte
--FILE--
<?php
try {
Uri\WhatWg\Url::parse("https://exam\0ple.com");
} catch (ValueError $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
ValueError: Uri\WhatWg\Url::parse(): Argument #1 ($uri) must not contain any null bytes

View File

@@ -5,10 +5,10 @@ Test Uri\WhatWg\Url parsing - basic - URL contains null byte
try {
new Uri\WhatWg\Url("https://exam\0ple.com");
} catch (ValueError $e) {
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
ValueError: Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes
Uri\WhatWg\InvalidUrlException: The specified URI is malformed (DomainInvalidCodePoint)

View File

@@ -0,0 +1,31 @@
--TEST--
Test Uri\WhatWg\Url parsing - password - null byte
--FILE--
<?php
$url = new Uri\WhatWg\Url("https://username:\0pass@example.com/");
var_dump($url);
var_dump($url->toAsciiString());
?>
--EXPECT--
object(Uri\WhatWg\Url)#1 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
string(8) "username"
["password"]=>
string(7) "%00pass"
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(1) "/"
["query"]=>
NULL
["fragment"]=>
NULL
}
string(37) "https://username:%00pass@example.com/"

View File

@@ -0,0 +1,31 @@
--TEST--
Test Uri\WhatWg\Url parsing - path - null byte
--FILE--
<?php
$url = new Uri\WhatWg\Url("https://example.com/pa\0th\0");
var_dump($url);
var_dump($url->toAsciiString());
?>
--EXPECT--
object(Uri\WhatWg\Url)#1 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
NULL
["password"]=>
NULL
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(8) "/pa%00th"
["query"]=>
NULL
["fragment"]=>
NULL
}
string(27) "https://example.com/pa%00th"

View File

@@ -0,0 +1,31 @@
--TEST--
Test Uri\WhatWg\Url parsing - query - null byte
--FILE--
<?php
$url = new Uri\WhatWg\Url("https://example.com/?fo\0=bar\0");
var_dump($url);
var_dump($url->toAsciiString());
?>
--EXPECT--
object(Uri\WhatWg\Url)#1 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
NULL
["password"]=>
NULL
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(1) "/"
["query"]=>
string(9) "fo%00=bar"
["fragment"]=>
NULL
}
string(30) "https://example.com/?fo%00=bar"

View File

@@ -0,0 +1,14 @@
--TEST--
Test Uri\WhatWg\Url parsing - scheme - null byte
--FILE--
<?php
try {
new Uri\WhatWg\Url("ht\0tp://example.com");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\WhatWg\InvalidUrlException: The specified URI is malformed (MissingSchemeNonRelativeUrl)

View File

@@ -0,0 +1,31 @@
--TEST--
Test Uri\WhatWg\Url parsing - username - null byte
--FILE--
<?php
$url = new Uri\WhatWg\Url("https://user\0name:pass@example.com/");
var_dump($url);
var_dump($url->toAsciiString());
?>
--EXPECT--
object(Uri\WhatWg\Url)#1 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
string(11) "user%00name"
["password"]=>
string(4) "pass"
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(1) "/"
["query"]=>
NULL
["fragment"]=>
NULL
}
string(37) "https://user%00name:pass@example.com/"

View File

@@ -0,0 +1,18 @@
--TEST--
Test Uri\WhatWg\Url reference resolution - resolve() - null byte
--EXTENSIONS--
uri
--FILE--
<?php
$url = Uri\WhatWg\Url::parse("https://example.com");
try {
$url->resolve("https://ex\0mple.com");
} catch (Throwable $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Uri\WhatWg\InvalidUrlException: The specified URI is malformed (DomainInvalidCodePoint)

View File

@@ -0,0 +1,53 @@
--TEST--
Test Uri\WhatWg\Url reference resolution - resolve() - null byte in path
--EXTENSIONS--
uri
--FILE--
<?php
$url1 = Uri\WhatWg\Url::parse("https://example.com");
$url2 = $url1->resolve("/f\0o");
var_dump($url1);
var_dump($url2);
var_dump($url2->toAsciiString());
?>
--EXPECT--
object(Uri\WhatWg\Url)#1 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
NULL
["password"]=>
NULL
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(1) "/"
["query"]=>
NULL
["fragment"]=>
NULL
}
object(Uri\WhatWg\Url)#2 (8) {
["scheme"]=>
string(5) "https"
["username"]=>
NULL
["password"]=>
NULL
["host"]=>
string(11) "example.com"
["port"]=>
NULL
["path"]=>
string(6) "/f%00o"
["query"]=>
NULL
["fragment"]=>
NULL
}
string(25) "https://example.com/f%00o"