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.
This commit is contained in:
Tim Starling 2022-01-21 12:08:19 +11:00 committed by Jakub Zelenka
parent 35e2a25d83
commit 11796229f2
No known key found for this signature in database
GPG Key ID: 1C0779DC5C0A9DE4
9 changed files with 94 additions and 26 deletions

3
NEWS
View File

@ -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:

View File

@ -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
========================================

View File

@ -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); \

View File

@ -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)
{

View File

@ -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 {}

View File

@ -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
};

View File

@ -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;

View File

@ -0,0 +1,36 @@
--TEST--
libxml_get_external_entity_loader() returns current handler
--EXTENSIONS--
libxml
--FILE--
<?php
class Handler {
private $name;
public function __construct($name) {
$this->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

View File

@ -0,0 +1,14 @@
--TEST--
libxml_set_external_entity_loader() with non-callable argument
--EXTENSIONS--
libxml
--FILE--
<?php
try {
libxml_set_external_entity_loader('nonexistent_function');
} catch (Throwable $e) {
echo "Exception: " . $e->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