mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
- Turns out the external entity loader is not a per-thread global but a true
global. Changed code accordingly; however, applications that embed PHP and also use libxml2 may be affected negatively.
This commit is contained in:
parent
8590f2587a
commit
7bd505021c
@ -55,6 +55,7 @@
|
||||
/* a true global for initialization */
|
||||
static int _php_libxml_initialized = 0;
|
||||
static int _php_libxml_per_request_initialization = 1;
|
||||
static xmlExternalEntityLoader _php_libxml_default_entity_loader;
|
||||
|
||||
typedef struct _php_libxml_func_handler {
|
||||
php_libxml_export_node export_func;
|
||||
@ -268,7 +269,6 @@ static PHP_GINIT_FUNCTION(libxml)
|
||||
libxml_globals->stream_context = NULL;
|
||||
libxml_globals->error_buffer.c = NULL;
|
||||
libxml_globals->error_list = NULL;
|
||||
libxml_globals->defaultEntityLoader = NULL;
|
||||
libxml_globals->entity_loader.fci.size = 0;
|
||||
}
|
||||
|
||||
@ -549,6 +549,131 @@ static void php_libxml_internal_error_handler(int error_type, void *ctx, const c
|
||||
}
|
||||
}
|
||||
|
||||
static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
|
||||
const char *ID, xmlParserCtxtPtr context)
|
||||
{
|
||||
xmlParserInputPtr ret = NULL;
|
||||
const char *resource = NULL;
|
||||
zval *public = NULL,
|
||||
*system = NULL,
|
||||
*ctxzv = NULL,
|
||||
**params[] = {&public, &system, &ctxzv},
|
||||
*retval_ptr = NULL;
|
||||
int retval;
|
||||
zend_fcall_info *fci;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
fci = &LIBXML(entity_loader).fci;
|
||||
|
||||
if (fci->size == 0) {
|
||||
/* no custom user-land callback set up; delegate to original loader */
|
||||
return _php_libxml_default_entity_loader(URL, ID, context);
|
||||
}
|
||||
|
||||
ALLOC_INIT_ZVAL(public);
|
||||
if (ID != NULL) {
|
||||
ZVAL_STRING(public, ID, 1);
|
||||
}
|
||||
ALLOC_INIT_ZVAL(system);
|
||||
if (URL != NULL) {
|
||||
ZVAL_STRING(system, URL, 1);
|
||||
}
|
||||
MAKE_STD_ZVAL(ctxzv);
|
||||
array_init_size(ctxzv, 4);
|
||||
|
||||
#define ADD_NULL_OR_STRING_KEY(memb) \
|
||||
if (context->memb == NULL) { \
|
||||
add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
|
||||
} else { \
|
||||
add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
|
||||
(char *)context->memb, 1); \
|
||||
}
|
||||
|
||||
ADD_NULL_OR_STRING_KEY(directory)
|
||||
ADD_NULL_OR_STRING_KEY(intSubName)
|
||||
ADD_NULL_OR_STRING_KEY(extSubURI)
|
||||
ADD_NULL_OR_STRING_KEY(extSubSystem)
|
||||
|
||||
#undef ADD_NULL_OR_STRING_KEY
|
||||
|
||||
fci->retval_ptr_ptr = &retval_ptr;
|
||||
fci->params = params;
|
||||
fci->param_count = sizeof(params)/sizeof(*params);
|
||||
fci->no_separation = 1;
|
||||
|
||||
retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
|
||||
if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"Call to user entity loader callback '%s' has failed",
|
||||
fci->function_name);
|
||||
} else {
|
||||
retval_ptr = *fci->retval_ptr_ptr;
|
||||
if (retval_ptr == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"Call to user entity loader callback '%s' has failed; "
|
||||
"probably it has thrown an exception",
|
||||
fci->function_name);
|
||||
} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
|
||||
is_string:
|
||||
resource = Z_STRVAL_P(retval_ptr);
|
||||
} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
|
||||
php_stream *stream;
|
||||
php_stream_from_zval_no_verify(stream, &retval_ptr);
|
||||
if (stream == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"The user entity loader callback '%s' has returned a "
|
||||
"resource, but it is not a stream",
|
||||
fci->function_name);
|
||||
} else {
|
||||
/* TODO: allow storing the encoding in the stream context? */
|
||||
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
|
||||
xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
|
||||
if (pib == NULL) {
|
||||
php_libxml_ctx_error(context, "Could not allocate parser "
|
||||
"input buffer");
|
||||
} else {
|
||||
/* make stream not being closed when the zval is freed */
|
||||
zend_list_addref(stream->rsrc_id);
|
||||
pib->context = stream;
|
||||
pib->readcallback = php_libxml_streams_IO_read;
|
||||
pib->closecallback = php_libxml_streams_IO_close;
|
||||
|
||||
ret = xmlNewIOInputStream(context, pib, enc);
|
||||
if (ret == NULL) {
|
||||
xmlFreeParserInputBuffer(pib);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
|
||||
/* retval not string nor resource nor null; convert to string */
|
||||
SEPARATE_ZVAL(&retval_ptr);
|
||||
convert_to_string(retval_ptr);
|
||||
goto is_string;
|
||||
} /* else is null; don't try anything */
|
||||
}
|
||||
|
||||
if (ret == NULL) {
|
||||
if (resource == NULL) {
|
||||
if (ID == NULL) {
|
||||
ID = "NULL";
|
||||
}
|
||||
php_libxml_ctx_error(context,
|
||||
"Failed to load external entity \"%s\"\n", ID);
|
||||
} else {
|
||||
/* we got the resource in the form of a string; open it */
|
||||
ret = xmlNewInputFromFile(context, resource);
|
||||
}
|
||||
}
|
||||
|
||||
zval_ptr_dtor(&public);
|
||||
zval_ptr_dtor(&system);
|
||||
zval_ptr_dtor(&ctxzv);
|
||||
if (retval_ptr != NULL) {
|
||||
zval_ptr_dtor(&retval_ptr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
@ -586,6 +711,9 @@ PHP_LIBXML_API void php_libxml_initialize(void)
|
||||
if (!_php_libxml_initialized) {
|
||||
/* we should be the only one's to ever init!! */
|
||||
xmlInitParser();
|
||||
|
||||
_php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
|
||||
xmlSetExternalEntityLoader(_php_libxml_external_entity_loader);
|
||||
|
||||
zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
|
||||
|
||||
@ -926,126 +1054,6 @@ static PHP_FUNCTION(libxml_disable_entity_loader)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static xmlParserInputPtr _php_libxml_user_entity_loader(const char *URL,
|
||||
const char *ID, xmlParserCtxtPtr context)
|
||||
{
|
||||
xmlParserInputPtr ret = NULL;
|
||||
const char *resource = NULL;
|
||||
zval *public = NULL,
|
||||
*system = NULL,
|
||||
*ctxzv = NULL,
|
||||
**params[] = {&public, &system, &ctxzv},
|
||||
*retval_ptr = NULL;
|
||||
int retval;
|
||||
zend_fcall_info *fci;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
fci = &LIBXML(entity_loader).fci;
|
||||
|
||||
ALLOC_INIT_ZVAL(public);
|
||||
if (ID != NULL) {
|
||||
ZVAL_STRING(public, ID, 1);
|
||||
}
|
||||
ALLOC_INIT_ZVAL(system);
|
||||
if (URL != NULL) {
|
||||
ZVAL_STRING(system, URL, 1);
|
||||
}
|
||||
MAKE_STD_ZVAL(ctxzv);
|
||||
array_init_size(ctxzv, 4);
|
||||
|
||||
#define ADD_NULL_OR_STRING_KEY(memb) \
|
||||
if (context->memb == NULL) { \
|
||||
add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
|
||||
} else { \
|
||||
add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
|
||||
(char *)context->memb, 1); \
|
||||
}
|
||||
|
||||
ADD_NULL_OR_STRING_KEY(directory)
|
||||
ADD_NULL_OR_STRING_KEY(intSubName)
|
||||
ADD_NULL_OR_STRING_KEY(extSubURI)
|
||||
ADD_NULL_OR_STRING_KEY(extSubSystem)
|
||||
|
||||
#undef ADD_NULL_OR_STRING_KEY
|
||||
|
||||
fci->retval_ptr_ptr = &retval_ptr;
|
||||
fci->params = params;
|
||||
fci->param_count = sizeof(params)/sizeof(*params);
|
||||
fci->no_separation = 1;
|
||||
|
||||
retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
|
||||
if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"Call to user entity loader callback '%s' has failed",
|
||||
fci->function_name);
|
||||
} else {
|
||||
retval_ptr = *fci->retval_ptr_ptr;
|
||||
if (retval_ptr == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"Call to user entity loader callback '%s' has failed; "
|
||||
"probably it has thrown an exception",
|
||||
fci->function_name);
|
||||
} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
|
||||
is_string:
|
||||
resource = Z_STRVAL_P(retval_ptr);
|
||||
} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
|
||||
php_stream *stream;
|
||||
php_stream_from_zval_no_verify(stream, &retval_ptr);
|
||||
if (stream == NULL) {
|
||||
php_libxml_ctx_error(context,
|
||||
"The user entity loader callback '%s' has returned a "
|
||||
"resource, but it is not a stream",
|
||||
fci->function_name);
|
||||
} else {
|
||||
/* TODO: allow storing the encoding in the stream context? */
|
||||
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
|
||||
xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
|
||||
if (pib == NULL) {
|
||||
php_libxml_ctx_error(context, "Could not allocate parser "
|
||||
"input buffer");
|
||||
} else {
|
||||
/* make stream not being closed when the zval is freed */
|
||||
zend_list_addref(stream->rsrc_id);
|
||||
pib->context = stream;
|
||||
pib->readcallback = php_libxml_streams_IO_read;
|
||||
pib->closecallback = php_libxml_streams_IO_close;
|
||||
|
||||
ret = xmlNewIOInputStream(context, pib, enc);
|
||||
if (ret == NULL) {
|
||||
xmlFreeParserInputBuffer(pib);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
|
||||
/* retval not string nor resource nor null; convert to string */
|
||||
SEPARATE_ZVAL(&retval_ptr);
|
||||
convert_to_string(retval_ptr);
|
||||
goto is_string;
|
||||
} /* else is null; don't try anything */
|
||||
}
|
||||
|
||||
if (ret == NULL) {
|
||||
if (resource == NULL) {
|
||||
if (ID == NULL) {
|
||||
ID = "NULL";
|
||||
}
|
||||
php_libxml_ctx_error(context,
|
||||
"Failed to load external entity \"%s\"\n", ID);
|
||||
} else {
|
||||
/* we got the resource in the form of a string; open it */
|
||||
ret = xmlNewInputFromFile(context, resource);
|
||||
}
|
||||
}
|
||||
|
||||
zval_ptr_dtor(&public);
|
||||
zval_ptr_dtor(&system);
|
||||
zval_ptr_dtor(&ctxzv);
|
||||
if (retval_ptr != NULL) {
|
||||
zval_ptr_dtor(&retval_ptr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
|
||||
Changes the default external entity loader */
|
||||
static PHP_FUNCTION(libxml_set_external_entity_loader)
|
||||
@ -1057,21 +1065,15 @@ static PHP_FUNCTION(libxml_set_external_entity_loader)
|
||||
return;
|
||||
}
|
||||
|
||||
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
|
||||
|
||||
if (fci.size > 0) { /* argument not null */
|
||||
/* save for later invocations with NULL */
|
||||
if (LIBXML(defaultEntityLoader) == NULL) {
|
||||
LIBXML(defaultEntityLoader) = xmlGetExternalEntityLoader();
|
||||
}
|
||||
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
|
||||
LIBXML(entity_loader).fci = fci;
|
||||
Z_ADDREF_P(fci.function_name);
|
||||
if (fci.object_ptr != NULL) {
|
||||
Z_ADDREF_P(fci.object_ptr);
|
||||
}
|
||||
LIBXML(entity_loader).fcc = fcc;
|
||||
xmlSetExternalEntityLoader(_php_libxml_user_entity_loader);
|
||||
} else {
|
||||
xmlSetExternalEntityLoader(LIBXML(defaultEntityLoader));
|
||||
}
|
||||
|
||||
RETURN_TRUE;
|
||||
|
@ -43,7 +43,6 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml)
|
||||
zval *stream_context;
|
||||
smart_str error_buffer;
|
||||
zend_llist *error_list;
|
||||
xmlExternalEntityLoader defaultEntityLoader; /* saved here to allow it restored */
|
||||
struct _php_libxml_entity_resolver {
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
|
Loading…
Reference in New Issue
Block a user