Skip to content

Commit 0466b35

Browse files
committed
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_GET_PREV_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.
1 parent b825756 commit 0466b35

File tree

7 files changed

+84
-25
lines changed

7 files changed

+84
-25
lines changed

Zend/zend_API.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,10 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
15281528
SEPARATE_ZVAL_NOREF(_arg); \
15291529
}
15301530

1531+
/* get the zval* for a previously parsed argument */
1532+
#define Z_PARAM_GET_PREV_ZVAL(dest) \
1533+
zend_parse_arg_zval_deref(_arg, &dest, 0);
1534+
15311535
/* old "|" */
15321536
#define Z_PARAM_OPTIONAL \
15331537
_optional = 1;

ext/libxml/libxml.c

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -228,23 +228,11 @@ static PHP_GINIT_FUNCTION(libxml)
228228
ZVAL_UNDEF(&libxml_globals->stream_context);
229229
libxml_globals->error_buffer.s = NULL;
230230
libxml_globals->error_list = NULL;
231-
ZVAL_UNDEF(&libxml_globals->entity_loader.object);
231+
ZVAL_NULL(&libxml_globals->entity_loader.callback);
232232
libxml_globals->entity_loader.fci.size = 0;
233233
libxml_globals->entity_loader_disabled = 0;
234234
}
235235

236-
static void _php_libxml_destroy_fci(zend_fcall_info *fci, zval *object)
237-
{
238-
if (fci->size > 0) {
239-
zval_ptr_dtor(&fci->function_name);
240-
fci->size = 0;
241-
}
242-
if (!Z_ISUNDEF_P(object)) {
243-
zval_ptr_dtor(object);
244-
ZVAL_UNDEF(object);
245-
}
246-
}
247-
248236
/* Channel libxml file io layer through the PHP streams subsystem.
249237
* This allows use of ftps:// and https:// urls */
250238

@@ -851,7 +839,9 @@ static PHP_RINIT_FUNCTION(libxml)
851839

852840
static PHP_RSHUTDOWN_FUNCTION(libxml)
853841
{
854-
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
842+
LIBXML(entity_loader).fci.size = 0;
843+
zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback);
844+
ZVAL_NULL(&LIBXML(entity_loader).callback);
855845

856846
return SUCCESS;
857847
}
@@ -1071,29 +1061,37 @@ PHP_FUNCTION(libxml_disable_entity_loader)
10711061
/* {{{ Changes the default external entity loader */
10721062
PHP_FUNCTION(libxml_set_external_entity_loader)
10731063
{
1064+
zval *callback;
10741065
zend_fcall_info fci;
10751066
zend_fcall_info_cache fcc;
10761067

10771068
ZEND_PARSE_PARAMETERS_START(1, 1)
10781069
Z_PARAM_FUNC_OR_NULL(fci, fcc)
1070+
Z_PARAM_GET_PREV_ZVAL(callback)
10791071
ZEND_PARSE_PARAMETERS_END();
10801072

1081-
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1082-
10831073
if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */
10841074
LIBXML(entity_loader).fci = fci;
1085-
Z_ADDREF(fci.function_name);
1086-
if (fci.object != NULL) {
1087-
ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1088-
Z_ADDREF(LIBXML(entity_loader).object);
1089-
}
10901075
LIBXML(entity_loader).fcc = fcc;
1076+
} else {
1077+
LIBXML(entity_loader).fci.size = 0;
10911078
}
1092-
1079+
if (!Z_ISNULL(LIBXML(entity_loader).callback)) {
1080+
zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback);
1081+
}
1082+
ZVAL_COPY(&LIBXML(entity_loader).callback, callback);
10931083
RETURN_TRUE;
10941084
}
10951085
/* }}} */
10961086

1087+
/* {{{ Get the current external entity loader, or null if the default loader is installer. */
1088+
PHP_FUNCTION(libxml_get_external_entity_loader)
1089+
{
1090+
ZEND_PARSE_PARAMETERS_NONE();
1091+
RETURN_COPY(&LIBXML(entity_loader).callback);
1092+
}
1093+
/* }}} */
1094+
10971095
/* {{{ Common functions shared by extensions */
10981096
int php_libxml_xmlCheckUTF8(const unsigned char *s)
10991097
{

ext/libxml/libxml.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,5 @@ function libxml_clear_errors(): void {}
177177
function libxml_disable_entity_loader(bool $disable = true): bool {}
178178

179179
function libxml_set_external_entity_loader(?callable $resolver_function): bool {}
180+
181+
function libxml_get_external_entity_loader(): ?callable {}

ext/libxml/libxml_arginfo.h

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/libxml/php_libxml.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml)
4343
smart_str error_buffer;
4444
zend_llist *error_list;
4545
struct _php_libxml_entity_resolver {
46-
zval object;
47-
zend_fcall_info fci;
46+
zval callback;
47+
zend_fcall_info fci;
4848
zend_fcall_info_cache fcc;
4949
} entity_loader;
5050
bool entity_loader_disabled;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
libxml_get_external_entity_loader() returns current handler
3+
--EXTENSIONS--
4+
libxml
5+
--FILE--
6+
<?php
7+
8+
class Handler {
9+
private $name;
10+
11+
public function __construct($name) {
12+
$this->name = $name;
13+
}
14+
15+
public function handle($public, $system, $context) {
16+
return null;
17+
}
18+
19+
public function __toString() {
20+
return "Handler#{$this->name}";
21+
}
22+
}
23+
24+
var_dump(libxml_get_external_entity_loader());
25+
libxml_set_external_entity_loader([new Handler('A'), 'handle']);
26+
print libxml_get_external_entity_loader()[0] . "\n";
27+
libxml_set_external_entity_loader([new Handler('B'), 'handle']);
28+
print libxml_get_external_entity_loader()[0] . "\n";
29+
libxml_set_external_entity_loader(null);
30+
var_dump(libxml_get_external_entity_loader());
31+
32+
--EXPECT--
33+
NULL
34+
Handler#A
35+
Handler#B
36+
NULL
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
libxml_set_external_entity_loader() with non-callable argument
3+
--EXTENSIONS--
4+
libxml
5+
--FILE--
6+
<?php
7+
try {
8+
libxml_set_external_entity_loader('nonexistent_function');
9+
} catch (Throwable $e) {
10+
echo "Exception: " . $e->getMessage() . "\n";
11+
}
12+
?>
13+
--EXPECT--
14+
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

0 commit comments

Comments
 (0)