Fix trampoline leak in xpath callables

Closes GH-15009.
This commit is contained in:
Niels Dossche 2024-07-18 15:17:42 +02:00
parent 5471f3d922
commit a59103feed
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
5 changed files with 54 additions and 4 deletions

6
NEWS
View file

@ -5,6 +5,9 @@ PHP NEWS
- Core: - Core:
. Fix GH-14978 (The xmlreader extension phpize build). (Peter Kokot) . Fix GH-14978 (The xmlreader extension phpize build). (Peter Kokot)
- DOM:
. Fix trampoline leak in xpath callables. (nielsdos)
- OpenSSL: - OpenSSL:
. Bumped minimum required OpenSSL version to 1.1.0. (cmb) . Bumped minimum required OpenSSL version to 1.1.0. (cmb)
@ -16,6 +19,9 @@ PHP NEWS
- Standard: - Standard:
. Fix references in request_parse_body() options array. (nielsdos) . Fix references in request_parse_body() options array. (nielsdos)
- XSL:
. Fix trampoline leak in xpath callables. (nielsdos)
18 Jul 2024, PHP 8.4.0alpha2 18 Jul 2024, PHP 8.4.0alpha2
- Core: - Core:

View file

@ -5,6 +5,11 @@ dom
--FILE-- --FILE--
<?php <?php
class TrampolineClass {
public function __call($name, $args) {
}
}
$doc = new DOMDocument(); $doc = new DOMDocument();
$doc->loadHTML('<a href="https://PHP.net">hello</a>'); $doc->loadHTML('<a href="https://PHP.net">hello</a>');
@ -16,6 +21,18 @@ try {
echo $e->getMessage(), "\n"; echo $e->getMessage(), "\n";
} }
try {
$xpath->registerPhpFunctionNS('http://php.net/xpath', 'test', [new TrampolineClass, 'test']);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
$xpath->registerPhpFunctionNS('urn:foo', '$$$', [new TrampolineClass, 'test']);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try { try {
$xpath->registerPhpFunctionNS('urn:foo', 'x:a', strtolower(...)); $xpath->registerPhpFunctionNS('urn:foo', 'x:a', strtolower(...));
} catch (ValueError $e) { } catch (ValueError $e) {
@ -37,6 +54,8 @@ try {
?> ?>
--EXPECT-- --EXPECT--
DOMXPath::registerPhpFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xpath" because it is reserved by PHP DOMXPath::registerPhpFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xpath" because it is reserved by PHP
DOMXPath::registerPhpFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xpath" because it is reserved by PHP
DOMXPath::registerPhpFunctionNS(): Argument #2 ($name) must be a valid callback name
DOMXPath::registerPhpFunctionNS(): Argument #2 ($name) must be a valid callback name DOMXPath::registerPhpFunctionNS(): Argument #2 ($name) must be a valid callback name
DOMXPath::registerPhpFunctionNS(): Argument #2 ($name) must not contain any null bytes DOMXPath::registerPhpFunctionNS(): Argument #2 ($name) must not contain any null bytes
DOMXPath::registerPhpFunctionNS(): Argument #1 ($namespaceURI) must not contain any null bytes DOMXPath::registerPhpFunctionNS(): Argument #1 ($namespaceURI) must not contain any null bytes

View file

@ -462,11 +462,12 @@ PHP_METHOD(DOMXPath, registerPhpFunctionNS)
ZEND_PARSE_PARAMETERS_END(); ZEND_PARSE_PARAMETERS_END();
if (zend_string_equals_literal(namespace, "http://php.net/xpath")) { if (zend_string_equals_literal(namespace, "http://php.net/xpath")) {
zend_release_fcall_info_cache(&fcc);
zend_argument_value_error(1, "must not be \"http://php.net/xpath\" because it is reserved by PHP"); zend_argument_value_error(1, "must not be \"http://php.net/xpath\" because it is reserved by PHP");
RETURN_THROWS(); RETURN_THROWS();
} }
php_dom_xpath_callbacks_update_single_method_handler( if (php_dom_xpath_callbacks_update_single_method_handler(
&intern->xpath_callbacks, &intern->xpath_callbacks,
intern->dom.ptr, intern->dom.ptr,
namespace, namespace,
@ -474,7 +475,9 @@ PHP_METHOD(DOMXPath, registerPhpFunctionNS)
&fcc, &fcc,
PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME, PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME,
dom_xpath_register_func_in_ctx dom_xpath_register_func_in_ctx
); ) != SUCCESS) {
zend_release_fcall_info_cache(&fcc);
}
} }
/* {{{ */ /* {{{ */

View file

@ -5,6 +5,11 @@ xsl
--FILE-- --FILE--
<?php <?php
class TrampolineClass {
public function __call($name, $args) {
}
}
require __DIR__ . '/xpath_callables.inc'; require __DIR__ . '/xpath_callables.inc';
try { try {
@ -13,6 +18,18 @@ try {
echo $e->getMessage(), "\n"; echo $e->getMessage(), "\n";
} }
try {
createProcessor([])->registerPhpFunctionNS('http://php.net/xsl', 'test', [new TrampolineClass, 'test']);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
createProcessor([])->registerPhpFunctionNS('urn:foo', '$$$', [new TrampolineClass, 'test']);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try { try {
createProcessor([])->registerPhpFunctionNS('urn:foo', 'x:a', strtolower(...)); createProcessor([])->registerPhpFunctionNS('urn:foo', 'x:a', strtolower(...));
} catch (ValueError $e) { } catch (ValueError $e) {
@ -34,6 +51,8 @@ try {
?> ?>
--EXPECT-- --EXPECT--
XSLTProcessor::registerPHPFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xsl" because it is reserved by PHP XSLTProcessor::registerPHPFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xsl" because it is reserved by PHP
XSLTProcessor::registerPHPFunctionNS(): Argument #1 ($namespaceURI) must not be "http://php.net/xsl" because it is reserved by PHP
XSLTProcessor::registerPHPFunctionNS(): Argument #2 ($name) must be a valid callback name
XSLTProcessor::registerPHPFunctionNS(): Argument #2 ($name) must be a valid callback name XSLTProcessor::registerPHPFunctionNS(): Argument #2 ($name) must be a valid callback name
XSLTProcessor::registerPHPFunctionNS(): Argument #2 ($name) must not contain any null bytes XSLTProcessor::registerPHPFunctionNS(): Argument #2 ($name) must not contain any null bytes
XSLTProcessor::registerPHPFunctionNS(): Argument #1 ($namespaceURI) must not contain any null bytes XSLTProcessor::registerPHPFunctionNS(): Argument #1 ($namespaceURI) must not contain any null bytes

View file

@ -707,11 +707,12 @@ PHP_METHOD(XSLTProcessor, registerPHPFunctionNS)
ZEND_PARSE_PARAMETERS_END(); ZEND_PARSE_PARAMETERS_END();
if (zend_string_equals_literal(namespace, "http://php.net/xsl")) { if (zend_string_equals_literal(namespace, "http://php.net/xsl")) {
zend_release_fcall_info_cache(&fcc);
zend_argument_value_error(1, "must not be \"http://php.net/xsl\" because it is reserved by PHP"); zend_argument_value_error(1, "must not be \"http://php.net/xsl\" because it is reserved by PHP");
RETURN_THROWS(); RETURN_THROWS();
} }
php_dom_xpath_callbacks_update_single_method_handler( if (php_dom_xpath_callbacks_update_single_method_handler(
&intern->xpath_callbacks, &intern->xpath_callbacks,
NULL, NULL,
namespace, namespace,
@ -719,7 +720,9 @@ PHP_METHOD(XSLTProcessor, registerPHPFunctionNS)
&fcc, &fcc,
PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME, PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME,
NULL NULL
); ) != SUCCESS) {
zend_release_fcall_info_cache(&fcc);
}
} }
/* {{{ */ /* {{{ */