From 11796229f2f047768ee4870328f8dfd7e1a315a6 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Fri, 21 Jan 2022 12:08:19 +1100 Subject: [PATCH] Add libxml_get_external_entity_loader() Add libxml_get_external_entity_loader(), which returns the currently installed external entity loader, i.e. the value which was passed to libxml_set_external_entity_loader() or null if no loader was installed and the default entity loader will be used. This allows libraries to save and restore the loader, controlling entity expansion without interfering with the rest of the application. Add macro Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(). This allows us to get the zval for a callable parameter without duplicating callable argument parsing. The saved zval keeps the object needed for fcc/fci alive, simplifying memory management. Fixes #76763. --- NEWS | 3 ++ UPGRADING | 3 ++ Zend/zend_API.h | 8 ++++ ext/libxml/libxml.c | 43 +++++++++---------- ext/libxml/libxml.stub.php | 2 + ext/libxml/libxml_arginfo.h | 7 ++- ext/libxml/php_libxml.h | 4 +- .../libxml_get_external_entity_loader.phpt | 36 ++++++++++++++++ ...xml_set_external_entity_loader_error2.phpt | 14 ++++++ 9 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 ext/libxml/tests/libxml_get_external_entity_loader.phpt create mode 100644 ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt diff --git a/NEWS b/NEWS index c34aa2388d0..9e7596f70ce 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,9 @@ PHP NEWS . Fixed bug GH-9316 ($http_response_header is wrong for long status line). (cmb, timwolla) +- XML: + . Added libxml_get_external_entity_loader() function. (Tim Starling) + 18 Aug 2022, PHP 8.2.0beta3 - Core: diff --git a/UPGRADING b/UPGRADING index aacbeea92df..6d1904b3994 100644 --- a/UPGRADING +++ b/UPGRADING @@ -251,6 +251,9 @@ PHP 8.2 UPGRADE NOTES ini_parse_quantity(). The `memory_limit` setting accepts values higher than PHP_INT_MAX, than can not be parsed by ini_parse_quantity(). +- XML: + . libxml_get_external_entity_loader() + ======================================== 7. New Classes and Interfaces ======================================== diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 5821a5b5f35..fa8c1162788 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1538,6 +1538,10 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * SEPARATE_ZVAL_NOREF(_arg); \ } +/* get the zval* for a previously parsed argument */ +#define Z_PARAM_GET_PREV_ZVAL(dest) \ + zend_parse_arg_zval_deref(_arg, &dest, 0); + /* old "|" */ #define Z_PARAM_OPTIONAL \ _optional = 1; @@ -1700,6 +1704,10 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * #define Z_PARAM_FUNC_OR_NULL(dest_fci, dest_fcc) \ Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) +#define Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(dest_fci, dest_fcc, dest_zp) \ + Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 1, 0) \ + Z_PARAM_GET_PREV_ZVAL(dest_zp) + /* old "h" */ #define Z_PARAM_ARRAY_HT_EX2(dest, check_null, deref, separate) \ Z_PARAM_PROLOGUE(deref, separate); \ diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 5813f437d22..270a0367481 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -228,23 +228,11 @@ static PHP_GINIT_FUNCTION(libxml) ZVAL_UNDEF(&libxml_globals->stream_context); libxml_globals->error_buffer.s = NULL; libxml_globals->error_list = NULL; - ZVAL_UNDEF(&libxml_globals->entity_loader.object); + ZVAL_NULL(&libxml_globals->entity_loader.callback); libxml_globals->entity_loader.fci.size = 0; libxml_globals->entity_loader_disabled = 0; } -static void _php_libxml_destroy_fci(zend_fcall_info *fci, zval *object) -{ - if (fci->size > 0) { - zval_ptr_dtor(&fci->function_name); - fci->size = 0; - } - if (!Z_ISUNDEF_P(object)) { - zval_ptr_dtor(object); - ZVAL_UNDEF(object); - } -} - /* Channel libxml file io layer through the PHP streams subsystem. * This allows use of ftps:// and https:// urls */ @@ -851,7 +839,9 @@ static PHP_RINIT_FUNCTION(libxml) static PHP_RSHUTDOWN_FUNCTION(libxml) { - _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); + LIBXML(entity_loader).fci.size = 0; + zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback); + ZVAL_NULL(&LIBXML(entity_loader).callback); return SUCCESS; } @@ -1071,29 +1061,36 @@ PHP_FUNCTION(libxml_disable_entity_loader) /* {{{ Changes the default external entity loader */ PHP_FUNCTION(libxml_set_external_entity_loader) { + zval *callback; zend_fcall_info fci; zend_fcall_info_cache fcc; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC_OR_NULL(fci, fcc) + Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(fci, fcc, callback) ZEND_PARSE_PARAMETERS_END(); - _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); - if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */ LIBXML(entity_loader).fci = fci; - Z_ADDREF(fci.function_name); - if (fci.object != NULL) { - ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object); - Z_ADDREF(LIBXML(entity_loader).object); - } LIBXML(entity_loader).fcc = fcc; + } else { + LIBXML(entity_loader).fci.size = 0; } - + if (!Z_ISNULL(LIBXML(entity_loader).callback)) { + zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback); + } + ZVAL_COPY(&LIBXML(entity_loader).callback, callback); RETURN_TRUE; } /* }}} */ +/* {{{ Get the current external entity loader, or null if the default loader is installer. */ +PHP_FUNCTION(libxml_get_external_entity_loader) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_COPY(&LIBXML(entity_loader).callback); +} +/* }}} */ + /* {{{ Common functions shared by extensions */ int php_libxml_xmlCheckUTF8(const unsigned char *s) { diff --git a/ext/libxml/libxml.stub.php b/ext/libxml/libxml.stub.php index 747e43d035b..97793f0cdee 100644 --- a/ext/libxml/libxml.stub.php +++ b/ext/libxml/libxml.stub.php @@ -177,3 +177,5 @@ function libxml_clear_errors(): void {} function libxml_disable_entity_loader(bool $disable = true): bool {} function libxml_set_external_entity_loader(?callable $resolver_function): bool {} + +function libxml_get_external_entity_loader(): ?callable {} diff --git a/ext/libxml/libxml_arginfo.h b/ext/libxml/libxml_arginfo.h index 58ca8c7540c..bd143cbed4d 100644 --- a/ext/libxml/libxml_arginfo.h +++ b/ext/libxml/libxml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d0c03ca63f0403ea38a59bde94510715e993b423 */ + * Stub hash: 8fa6f4fa2f3eb8db944626b5b3d814f5e3ae04a3 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_streams_context, 0, 1, IS_VOID, 0) ZEND_ARG_INFO(0, context) @@ -26,6 +26,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_set_external_entity_loade ZEND_ARG_TYPE_INFO(0, resolver_function, IS_CALLABLE, 1) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_libxml_get_external_entity_loader, 0, 0, IS_CALLABLE, 1) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(libxml_set_streams_context); ZEND_FUNCTION(libxml_use_internal_errors); @@ -34,6 +37,7 @@ ZEND_FUNCTION(libxml_get_errors); ZEND_FUNCTION(libxml_clear_errors); ZEND_FUNCTION(libxml_disable_entity_loader); ZEND_FUNCTION(libxml_set_external_entity_loader); +ZEND_FUNCTION(libxml_get_external_entity_loader); static const zend_function_entry ext_functions[] = { @@ -44,6 +48,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(libxml_clear_errors, arginfo_libxml_clear_errors) ZEND_DEP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader) ZEND_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader) + ZEND_FE(libxml_get_external_entity_loader, arginfo_libxml_get_external_entity_loader) ZEND_FE_END }; diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 0e014f451af..96217fd7786 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -43,8 +43,8 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml) smart_str error_buffer; zend_llist *error_list; struct _php_libxml_entity_resolver { - zval object; - zend_fcall_info fci; + zval callback; + zend_fcall_info fci; zend_fcall_info_cache fcc; } entity_loader; bool entity_loader_disabled; diff --git a/ext/libxml/tests/libxml_get_external_entity_loader.phpt b/ext/libxml/tests/libxml_get_external_entity_loader.phpt new file mode 100644 index 00000000000..48751d5cf2d --- /dev/null +++ b/ext/libxml/tests/libxml_get_external_entity_loader.phpt @@ -0,0 +1,36 @@ +--TEST-- +libxml_get_external_entity_loader() returns current handler +--EXTENSIONS-- +libxml +--FILE-- +name = $name; + } + + public function handle($public, $system, $context) { + return null; + } + + public function __toString() { + return "Handler#{$this->name}"; + } +} + +var_dump(libxml_get_external_entity_loader()); +libxml_set_external_entity_loader([new Handler('A'), 'handle']); +print libxml_get_external_entity_loader()[0] . "\n"; +libxml_set_external_entity_loader([new Handler('B'), 'handle']); +print libxml_get_external_entity_loader()[0] . "\n"; +libxml_set_external_entity_loader(null); +var_dump(libxml_get_external_entity_loader()); + +--EXPECT-- +NULL +Handler#A +Handler#B +NULL diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt new file mode 100644 index 00000000000..053551f5ff3 --- /dev/null +++ b/ext/libxml/tests/libxml_set_external_entity_loader_error2.phpt @@ -0,0 +1,14 @@ +--TEST-- +libxml_set_external_entity_loader() with non-callable argument +--EXTENSIONS-- +libxml +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Exception: libxml_set_external_entity_loader(): Argument #1 ($resolver_function) must be a valid callback or null, function "nonexistent_function" not found or invalid function name