From e504ab778c1d092b1d626f9df2e1d07aaef461ec Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:20:52 +0100 Subject: [PATCH] Fix memory leak when edge case is hit when registering xpath callback This can happen if you have a valid callable name with a NUL byte in it, on a non-interned string entry. This can be done by abusing anonymous classes. Closes GH-20452. --- NEWS | 4 ++++ ext/dom/tests/DOMXPath_callables_errors.phpt | 14 ++++++++++++++ ext/dom/xpath_callbacks.c | 6 ++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index b2f7d7956c3..f7b917410d8 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS . Fix crashes when trying to instantiate uninstantiable classes via date static constructors. (ndossche) +- DOM: + . Fix memory leak when edge case is hit when registering xpath callback. + (ndossche) + - Opcache: . Fixed bug GH-20329 (opcache.file_cache broken with full interned string buffer). (Arnaud) diff --git a/ext/dom/tests/DOMXPath_callables_errors.phpt b/ext/dom/tests/DOMXPath_callables_errors.phpt index d11437398b1..10019474e7e 100644 --- a/ext/dom/tests/DOMXPath_callables_errors.phpt +++ b/ext/dom/tests/DOMXPath_callables_errors.phpt @@ -57,6 +57,19 @@ try { echo $e->getMessage(), "\n"; } +$x = new class { + public static function dump() {} +}; + +$classes = get_declared_classes(); + +try { + $str = str_repeat($classes[count($classes) - 1] . '::dump', random_int(1, 1)); + $xpath->registerPhpFunctions([$str]); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECT-- DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be a callable, function "nonexistent" not found or invalid function name @@ -67,3 +80,4 @@ DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array with DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be a valid callback name +DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be an array containing valid callback names diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 283ccd8f7cc..0bd6c3f3318 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -206,14 +206,16 @@ static zend_result php_dom_xpath_callback_ns_update_method_handler( ZVAL_PTR(®istered_value, fcc); if (!key) { - zend_string *str = zval_try_get_string(entry); + zend_string *tmp_str; + zend_string *str = zval_try_get_tmp_string(entry, &tmp_str); if (str && php_dom_xpath_is_callback_name_valid_and_throw(str, name_validation, true)) { zend_hash_update(&ns->functions, str, ®istered_value); if (register_func) { register_func(ctxt, namespace, str); } - zend_string_release_ex(str, false); + zend_tmp_string_release(tmp_str); } else { + zend_tmp_string_release(tmp_str); zend_fcc_dtor(fcc); efree(fcc); return FAILURE;