diff --git a/NEWS b/NEWS index 60f1365e696f2..daa18a857d66b 100644 --- a/NEWS +++ b/NEWS @@ -236,5 +236,7 @@ PHP NEWS . Implement request #64137 (XSLTProcessor::setParameter() should allow both quotes to be used). (nielsdos) . Implemented "Improve callbacks in ext/dom and ext/xsl" RFC. (nielsdos) + . Added XSLTProcessor::$maxTemplateDepth and XSLTProcessor::$maxTemplateVars. + (nielsdos) <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/UPGRADING b/UPGRADING index 395e02e15338f..96b99dc393b5e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -261,6 +261,8 @@ PHP 8.4 UPGRADE NOTES quotes. . It is now possible to pass any callable to registerPhpFunctions(). RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl + . Added XSLTProcessor::$maxTemplateDepth and XSLTProcessor::$maxTemplateVars + to control the recursion depth of XSL template evaluation. ======================================== 3. Changes in SAPI modules diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 6be15a7f9dcf0..4928592134af6 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -192,6 +192,8 @@ PHP 8.4 INTERNALS UPGRADE NOTES d. ext/libxml - Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come from libxml. + - Added php_libxml_error_handler_va() to pass libxml errors, and + corresponding php_libxml_error_level enum. - Removed the "properties" HashTable field from php_libxml_node_object. - Added a way to attached private data to a php_libxml_ref_obj. - Added a way to fix a class type onto php_libxml_ref_obj. diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index a4dbc4e3f8c8c..3c67534514c04 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -67,8 +67,9 @@ PHP_DOM_EXPORT void php_dom_xpath_callbacks_clean_argument_stack(xmlXPathParserC xmlXPathFreeObject(obj); } - /* Push sentinel value */ - valuePush(ctxt, xmlXPathNewString((const xmlChar *) "")); + /* Don't push a sentinel value here. If this is called from an error situation, then by *not* pushing a sentinel + * the execution will halt. If this is called from a regular situation, then it is the caller's responsibility + * to ensure the stack remains balanced. */ } PHP_DOM_EXPORT void php_dom_xpath_callbacks_dtor(php_dom_xpath_callbacks *registry) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 9c94cb9da59fe..dc63f2d03858c 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -45,9 +45,6 @@ #include "php_libxml.h" #define PHP_LIBXML_LOADED_VERSION ((char *)xmlParserVersion) -#define PHP_LIBXML_ERROR 0 -#define PHP_LIBXML_CTX_ERROR 1 -#define PHP_LIBXML_CTX_WARNING 2 #include "libxml_arginfo.h" @@ -647,12 +644,12 @@ void php_libxml_issue_error(int level, const char *msg) } } -static void php_libxml_internal_error_handler_ex(int error_type, void *ctx, const char **msg, va_list ap, int line, int column) +static void php_libxml_internal_error_handler_ex(php_libxml_error_level error_type, void *ctx, const char *msg, va_list ap, int line, int column) { char *buf; int len, len_iter, output = 0; - len = vspprintf(&buf, 0, *msg, ap); + len = vspprintf(&buf, 0, msg, ap); len_iter = len; /* remove any trailing \n */ @@ -685,7 +682,7 @@ static void php_libxml_internal_error_handler_ex(int error_type, void *ctx, cons } } -static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap) +PHP_LIBXML_API void php_libxml_error_handler_va(php_libxml_error_level error_type, void *ctx, const char *msg, va_list ap) { int line = 0; int column = 0; @@ -831,7 +828,7 @@ PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, { va_list args; va_start(args, msg); - php_libxml_internal_error_handler_ex(PHP_LIBXML_CTX_ERROR, NULL, &msg, args, line, column); + php_libxml_internal_error_handler_ex(PHP_LIBXML_CTX_ERROR, NULL, msg, args, line, column); va_end(args); /* Propagate back into libxml */ @@ -853,7 +850,7 @@ PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...) { va_list args; va_start(args, msg); - php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args); + php_libxml_error_handler_va(PHP_LIBXML_CTX_ERROR, ctx, msg, args); va_end(args); } @@ -861,7 +858,7 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...) { va_list args; va_start(args, msg); - php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args); + php_libxml_error_handler_va(PHP_LIBXML_CTX_WARNING, ctx, msg, args); va_end(args); } @@ -878,7 +875,7 @@ PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...) { va_list args; va_start(args, msg); - php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args); + php_libxml_error_handler_va(PHP_LIBXML_ERROR, ctx, msg, args); va_end(args); } diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index 42bfb2e0fe2b3..b9262f5e96944 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -141,6 +141,12 @@ static zend_always_inline void php_libxml_invalidate_node_list_cache_from_doc(xm typedef void * (*php_libxml_export_node) (zval *object); +typedef enum { + PHP_LIBXML_ERROR = 0, + PHP_LIBXML_CTX_ERROR = 1, + PHP_LIBXML_CTX_WARNING = 2, +} php_libxml_error_level; + PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data); PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object); PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp); @@ -157,6 +163,7 @@ PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...); PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...); PHP_LIBXML_API void php_libxml_pretend_ctx_error_ex(const char *file, int line, int column, const char *msg,...); PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...); +PHP_LIBXML_API void php_libxml_error_handler_va(php_libxml_error_level error_type, void *ctx, const char *msg, va_list args); PHP_LIBXML_API int php_libxml_xmlCheckUTF8(const unsigned char *s); PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext); PHP_LIBXML_API void php_libxml_issue_error(int level, const char *msg); diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 5beccee2e62ab..605a933c286a1 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -118,10 +118,152 @@ zend_object *xsl_objects_new(zend_class_entry *class_type) intern->parameter = zend_new_array(0); php_dom_xpath_callbacks_ctor(&intern->xpath_callbacks); + /* Default initialize properties that could not be default initialized at the stub because they depend on library + * configuration parameters. */ + ZVAL_LONG(xsl_prop_max_template_depth(&intern->std), xsltMaxDepth); + ZVAL_LONG(xsl_prop_max_template_vars(&intern->std), xsltMaxVars); + return &intern->std; } /* }}} */ +#if ZEND_DEBUG +# define XSL_DEFINE_PROP_ACCESSOR(c_name, php_name, prop_index) \ + zval *xsl_prop_##c_name(zend_object *object) \ + { \ + zend_string *prop_name = ZSTR_INIT_LITERAL(php_name, false); \ + const zend_property_info *prop_info = zend_get_property_info(xsl_xsltprocessor_class_entry, prop_name, 0); \ + zend_string_release_ex(prop_name, false); \ + ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_index); \ + return OBJ_PROP_NUM(object, prop_index); \ + } +#else +# define XSL_DEFINE_PROP_ACCESSOR(c_name, php_name, prop_index) \ + zval *xsl_prop_##c_name(zend_object *object) \ + { \ + return OBJ_PROP_NUM(object, prop_index); \ + } +#endif + +XSL_DEFINE_PROP_ACCESSOR(max_template_depth, "maxTemplateDepth", 2) +XSL_DEFINE_PROP_ACCESSOR(max_template_vars, "maxTemplateVars", 3) + +static zval *xsl_objects_write_property_with_validation(zend_object *object, zend_string *member, zval *value, void **cache_slot, zval *property) +{ + /* Read old value so we can restore it if necessary. The value is not refcounted as its type is IS_LONG. */ + ZEND_ASSERT(Z_TYPE_P(property) == IS_LONG); + zend_long old_property_value = Z_LVAL_P(property); + + /* Write new property, which will also potentially perform coercions. */ + zend_std_write_property(object, member, value, NULL); + + /* Validate value *after* coercions have been performed, and restore the old value if necessary. */ + if (UNEXPECTED(Z_LVAL_P(property) < 0)) { + Z_LVAL_P(property) = old_property_value; + zend_value_error("%s::$%s must be greater than or equal to 0", ZSTR_VAL(object->ce->name), ZSTR_VAL(member)); + return &EG(error_zval); + } + + return property; +} + +static zval *xsl_objects_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot) +{ + /* Extra validation for maxTemplateDepth and maxTemplateVars */ + if (zend_string_equals_literal(member, "maxTemplateDepth")) { + zval *property = xsl_prop_max_template_depth(object); + return xsl_objects_write_property_with_validation(object, member, value, cache_slot, property); + } else if (zend_string_equals_literal(member, "maxTemplateVars")) { + zval *property = xsl_prop_max_template_vars(object); + return xsl_objects_write_property_with_validation(object, member, value, cache_slot, property); + } else { + return zend_std_write_property(object, member, value, cache_slot); + } +} + +static bool xsl_is_validated_property(const zend_string *member) +{ + return zend_string_equals_literal(member, "maxTemplateDepth") || zend_string_equals_literal(member, "maxTemplateVars"); +} + +static zval *xsl_objects_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) +{ + if (xsl_is_validated_property(member)) { + return NULL; + } + + return zend_std_get_property_ptr_ptr(object, member, type, cache_slot); +} + +static zval *xsl_objects_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) +{ + /* read handler is being called as a fallback after get_property_ptr_ptr returned NULL */ + if (type != BP_VAR_IS && type != BP_VAR_R && xsl_is_validated_property(member)) { + zend_throw_error(NULL, "Indirect modification of %s::$%s is not allowed", ZSTR_VAL(object->ce->name), ZSTR_VAL(member)); + return &EG(uninitialized_zval); + } + + return zend_std_read_property(object, member, type, cache_slot, rv); +} + +static void xsl_objects_unset_property(zend_object *object, zend_string *member, void **cache_slot) +{ + if (xsl_is_validated_property(member)) { + zend_throw_error(NULL, "Cannot unset %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(member)); + return; + } + + zend_std_unset_property(object, member, cache_slot); +} + +/* Tries to output an error message where a part was replaced by another string. + * Returns true if the search string was found and the error message with replacement was outputted. + * Return false otherwise. */ +static bool xsl_try_output_replaced_error_message( + void *ctx, + const char *msg, + va_list args, + const char *search, + size_t search_len, + const char *replace +) +{ + const char *msg_replace_location = strstr(msg, search); + if (msg_replace_location != NULL) { + php_libxml_ctx_error(ctx, "%.*s%s%s", (int) (msg_replace_location - msg), msg, replace, msg_replace_location + search_len); + return true; + } + return false; +} + +/* Helper macro so the string length doesn't need to be passed separately. + * Only allows literal strings for `search` and `replace`. */ +#define XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg, args, search, replace) \ + xsl_try_output_replaced_error_message(ctx, msg, args, "" search, sizeof("" search) - 1, "" replace) + +/* We want to output PHP-tailored error messages for some libxslt error messages, such that + * the errors refer to PHP properties instead of libxslt-specific fields. */ +static void xsl_libxslt_error_handler(void *ctx, const char *msg, ...) +{ + va_list args; + va_start(args, msg); + + if (strcmp(msg, "%s") == 0) { + /* Adjust error message to be more descriptive */ + const char *msg_arg = va_arg(args, const char *); + bool output = XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg_arg, args, "xsltMaxDepth (--maxdepth)", "$maxTemplateDepth") + || XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE(ctx, msg_arg, args, "maxTemplateVars (--maxvars)", "$maxTemplateVars"); + + if (!output) { + php_libxml_ctx_error(ctx, "%s", msg_arg); + } + } else { + php_libxml_error_handler_va(PHP_LIBXML_ERROR, ctx, msg, args); + } + + va_end(args); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(xsl) { @@ -130,6 +272,10 @@ PHP_MINIT_FUNCTION(xsl) xsl_object_handlers.clone_obj = NULL; xsl_object_handlers.free_obj = xsl_objects_free_storage; xsl_object_handlers.get_gc = xsl_objects_get_gc; + xsl_object_handlers.write_property = xsl_objects_write_property; + xsl_object_handlers.get_property_ptr_ptr = xsl_objects_get_property_ptr_ptr; + xsl_object_handlers.read_property = xsl_objects_read_property; + xsl_object_handlers.unset_property = xsl_objects_unset_property; xsl_xsltprocessor_class_entry = register_class_XSLTProcessor(); xsl_xsltprocessor_class_entry->create_object = xsl_objects_new; @@ -145,7 +291,7 @@ PHP_MINIT_FUNCTION(xsl) xsltRegisterExtModuleFunction ((const xmlChar *) "function", (const xmlChar *) "http://php.net/xsl", xsl_ext_function_object_php); - xsltSetGenericErrorFunc(NULL, php_libxml_error_handler); + xsltSetGenericErrorFunc(NULL, xsl_libxslt_error_handler); register_php_xsl_symbols(module_number); diff --git a/ext/xsl/php_xsl.h b/ext/xsl/php_xsl.h index accdecf46bdb2..8590b7ad92222 100644 --- a/ext/xsl/php_xsl.h +++ b/ext/xsl/php_xsl.h @@ -77,6 +77,9 @@ void xsl_objects_free_storage(zend_object *object); void xsl_ext_function_string_php(xmlXPathParserContextPtr ctxt, int nargs); void xsl_ext_function_object_php(xmlXPathParserContextPtr ctxt, int nargs); +zval *xsl_prop_max_template_depth(zend_object *object); +zval *xsl_prop_max_template_vars(zend_object *object); + PHP_MINIT_FUNCTION(xsl); PHP_MSHUTDOWN_FUNCTION(xsl); PHP_RINIT_FUNCTION(xsl); diff --git a/ext/xsl/php_xsl.stub.php b/ext/xsl/php_xsl.stub.php index fffcc5dfc93c7..c01e218162eef 100644 --- a/ext/xsl/php_xsl.stub.php +++ b/ext/xsl/php_xsl.stub.php @@ -75,6 +75,10 @@ class XSLTProcessor public bool $cloneDocument = false; + public int $maxTemplateDepth; + + public int $maxTemplateVars; + /** * @param DOMDocument|DOM\Document|SimpleXMLElement $stylesheet * @tentative-return-type diff --git a/ext/xsl/php_xsl_arginfo.h b/ext/xsl/php_xsl_arginfo.h index 2d8427c95f841..033e1e8e3b21f 100644 --- a/ext/xsl/php_xsl_arginfo.h +++ b/ext/xsl/php_xsl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ae093276a1caf53c34d429076458bc8b802d69e9 */ + * Stub hash: f7f9951cbb437f1985016dc06490d55f711bc725 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_XSLTProcessor_importStylesheet, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, stylesheet, IS_OBJECT, 0) @@ -131,5 +131,17 @@ static zend_class_entry *register_class_XSLTProcessor(void) zend_declare_typed_property(class_entry, property_cloneDocument_name, &property_cloneDocument_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); zend_string_release(property_cloneDocument_name); + zval property_maxTemplateDepth_default_value; + ZVAL_UNDEF(&property_maxTemplateDepth_default_value); + zend_string *property_maxTemplateDepth_name = zend_string_init("maxTemplateDepth", sizeof("maxTemplateDepth") - 1, 1); + zend_declare_typed_property(class_entry, property_maxTemplateDepth_name, &property_maxTemplateDepth_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_maxTemplateDepth_name); + + zval property_maxTemplateVars_default_value; + ZVAL_UNDEF(&property_maxTemplateVars_default_value); + zend_string *property_maxTemplateVars_name = zend_string_init("maxTemplateVars", sizeof("maxTemplateVars") - 1, 1); + zend_declare_typed_property(class_entry, property_maxTemplateVars_name, &property_maxTemplateVars_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_maxTemplateVars_name); + return class_entry; } diff --git a/ext/xsl/tests/bug71571_a.phpt b/ext/xsl/tests/bug71571_a.phpt new file mode 100644 index 0000000000000..ee19a91896508 --- /dev/null +++ b/ext/xsl/tests/bug71571_a.phpt @@ -0,0 +1,48 @@ +--TEST-- +Request #71571 (XSLT processor should provide option to change maxDepth) - variant A +--EXTENSIONS-- +xsl +--INI-- +error_reporting=E_ALL +--FILE-- + + + + + + + + + + +EOF; + +$xsl = new DOMDocument(); +$xsl->loadXML($myxsl); + +$doc = new DOMDocument(); + +$proc = new XSLTProcessor; +$proc->maxTemplateDepth = 2; +$proc->importStyleSheet($xsl); +$proc->transformToDoc($doc); + +?> +--EXPECTF-- +Warning: XSLTProcessor::transformToDoc(): runtime error: file %s line 8 element call-template in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): xsltApplySequenceConstructor: A potential infinite template recursion was detected. +You can adjust $maxTemplateDepth in order to raise the maximum number of nested template calls and variables/params (currently set to 2). in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): Templates: in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #0 name recurse in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #1 name recurse in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #2 name / in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): Variables: in %s on line %d diff --git a/ext/xsl/tests/bug71571_b.phpt b/ext/xsl/tests/bug71571_b.phpt new file mode 100644 index 0000000000000..b40076bfd368f --- /dev/null +++ b/ext/xsl/tests/bug71571_b.phpt @@ -0,0 +1,61 @@ +--TEST-- +Request #71571 (XSLT processor should provide option to change maxDepth) - variant B +--EXTENSIONS-- +xsl +--INI-- +error_reporting=E_ALL +--FILE-- + + + + + + + + 1 + + + + + +EOF; + +$xsl = new DOMDocument(); +$xsl->loadXML($myxsl); + +$doc = new DOMDocument(); + +$proc = new XSLTProcessor; +// Set the template depth limit so high that we will certainly hit the variable depth limit first. +$proc->maxTemplateDepth = 2**30; +$proc->maxTemplateVars = 2; +$proc->importStyleSheet($xsl); +$proc->transformToDoc($doc); + +?> +--EXPECTF-- +Warning: XSLTProcessor::transformToDoc(): runtime error: file %s line 8 element param in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): xsltApplyXSLTTemplate: A potential infinite template recursion was detected. +You can adjust $maxTemplateVars in order to raise the maximum number of variables/params (currently set to 2). in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): Templates: in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #0 name recurse in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #1 name recurse in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #2 name / in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): Variables: in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #0 in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): COUNT in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): #1 in %s on line %d + +Warning: XSLTProcessor::transformToDoc(): param COUNT in %s on line %d diff --git a/ext/xsl/tests/maxTemplateDepth_errors.phpt b/ext/xsl/tests/maxTemplateDepth_errors.phpt new file mode 100644 index 0000000000000..ec70927ea9c2d --- /dev/null +++ b/ext/xsl/tests/maxTemplateDepth_errors.phpt @@ -0,0 +1,44 @@ +--TEST-- +XSLTProcessor::$maxTemplateDepth errors +--EXTENSIONS-- +xsl +--INI-- +error_reporting=E_ALL & ~E_DEPRECATED +--FILE-- +maxTemplateDepth; + +try { + $processor->maxTemplateDepth = -1; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateDepth === $oldValue); + +try { + $processor->maxTemplateDepth = -32.1; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateDepth === $oldValue); + +try { + $processor->maxTemplateDepth = "-1"; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateDepth === $oldValue); + +?> +--EXPECT-- +XSLTProcessor::$maxTemplateDepth must be greater than or equal to 0 +bool(true) +XSLTProcessor::$maxTemplateDepth must be greater than or equal to 0 +bool(true) +XSLTProcessor::$maxTemplateDepth must be greater than or equal to 0 +bool(true) diff --git a/ext/xsl/tests/maxTemplateDepth_modification_validation_bypass.phpt b/ext/xsl/tests/maxTemplateDepth_modification_validation_bypass.phpt new file mode 100644 index 0000000000000..48305a512bd8a --- /dev/null +++ b/ext/xsl/tests/maxTemplateDepth_modification_validation_bypass.phpt @@ -0,0 +1,57 @@ +--TEST-- +XSLTProcessor::$maxTemplateDepth modification validation bypass +--EXTENSIONS-- +xsl +--FILE-- +maxTemplateDepth = $value; + } catch (ValueError $e) { + echo $e->getMessage(), "\n"; + } +} + +echo "--- Get reference ---\n"; + +try { + $ref =& $proc->maxTemplateDepth; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Unset ---\n"; + +try { + unset($proc->maxTemplateDepth); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Dump ---\n"; + +var_dump($proc); + +?> +--EXPECT-- +--- Set to 1 --- +--- Set to -1 --- +XSLTProcessor::$maxTemplateDepth must be greater than or equal to 0 +--- Get reference --- +Indirect modification of XSLTProcessor::$maxTemplateDepth is not allowed +--- Unset --- +Cannot unset XSLTProcessor::$maxTemplateDepth +--- Dump --- +object(XSLTProcessor)#1 (4) { + ["doXInclude"]=> + bool(false) + ["cloneDocument"]=> + bool(false) + ["maxTemplateDepth"]=> + int(1) + ["maxTemplateVars"]=> + int(15000) +} diff --git a/ext/xsl/tests/maxTemplateVars_errors.phpt b/ext/xsl/tests/maxTemplateVars_errors.phpt new file mode 100644 index 0000000000000..7ad9c7241e74b --- /dev/null +++ b/ext/xsl/tests/maxTemplateVars_errors.phpt @@ -0,0 +1,44 @@ +--TEST-- +XSLTProcessor::$maxTemplateVars errors +--EXTENSIONS-- +xsl +--INI-- +error_reporting=E_ALL & ~E_DEPRECATED +--FILE-- +maxTemplateVars; + +try { + $processor->maxTemplateVars = -1; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateVars === $oldValue); + +try { + $processor->maxTemplateVars = -32.1; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateVars === $oldValue); + +try { + $processor->maxTemplateVars = "-1"; +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($processor->maxTemplateVars === $oldValue); + +?> +--EXPECT-- +XSLTProcessor::$maxTemplateVars must be greater than or equal to 0 +bool(true) +XSLTProcessor::$maxTemplateVars must be greater than or equal to 0 +bool(true) +XSLTProcessor::$maxTemplateVars must be greater than or equal to 0 +bool(true) diff --git a/ext/xsl/tests/maxTemplateVars_modification_validation_bypass.phpt b/ext/xsl/tests/maxTemplateVars_modification_validation_bypass.phpt new file mode 100644 index 0000000000000..d2e52aa6099ba --- /dev/null +++ b/ext/xsl/tests/maxTemplateVars_modification_validation_bypass.phpt @@ -0,0 +1,57 @@ +--TEST-- +XSLTProcessor::$maxTemplateVars modification validation bypass +--EXTENSIONS-- +xsl +--FILE-- +maxTemplateVars = $value; + } catch (ValueError $e) { + echo $e->getMessage(), "\n"; + } +} + +echo "--- Get reference ---\n"; + +try { + $ref =& $proc->maxTemplateVars; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Unset ---\n"; + +try { + unset($proc->maxTemplateVars); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Dump ---\n"; + +var_dump($proc); + +?> +--EXPECT-- +--- Set to 1 --- +--- Set to -1 --- +XSLTProcessor::$maxTemplateVars must be greater than or equal to 0 +--- Get reference --- +Indirect modification of XSLTProcessor::$maxTemplateVars is not allowed +--- Unset --- +Cannot unset XSLTProcessor::$maxTemplateVars +--- Dump --- +object(XSLTProcessor)#1 (4) { + ["doXInclude"]=> + bool(false) + ["cloneDocument"]=> + bool(false) + ["maxTemplateDepth"]=> + int(3000) + ["maxTemplateVars"]=> + int(1) +} diff --git a/ext/xsl/tests/new_without_constructor.phpt b/ext/xsl/tests/new_without_constructor.phpt new file mode 100644 index 0000000000000..aa1de62758685 --- /dev/null +++ b/ext/xsl/tests/new_without_constructor.phpt @@ -0,0 +1,23 @@ +--TEST-- +XSLTProcessor: new instance without constructor +--EXTENSIONS-- +xsl +--FILE-- +newInstanceWithoutConstructor(); +var_dump($processor); + +?> +--EXPECTF-- +object(XSLTProcessor)#2 (4) { + ["doXInclude"]=> + bool(false) + ["cloneDocument"]=> + bool(false) + ["maxTemplateDepth"]=> + int(%d) + ["maxTemplateVars"]=> + int(%d) +} diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index aa5fbcd0a630a..7d0f9b05546d2 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -359,6 +359,14 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl ctxt->xinclude = zend_is_true(doXInclude); zend_string_release_ex(member, 0); + zval *max_template_depth = xsl_prop_max_template_depth(Z_OBJ_P(id)); + ZEND_ASSERT(Z_TYPE_P(max_template_depth) == IS_LONG); + ctxt->maxTemplateDepth = Z_LVAL_P(max_template_depth); + + zval *max_template_vars = xsl_prop_max_template_vars(Z_OBJ_P(id)); + ZEND_ASSERT(Z_TYPE_P(max_template_vars) == IS_LONG); + ctxt->maxTemplateVars = Z_LVAL_P(max_template_vars); + secPrefsValue = intern->securityPrefs; /* if securityPrefs is set to NONE, we don't have to do any checks, but otherwise... */