Commit 92e2fd60929 for php
commit 92e2fd60929ad85b1db2f653d9b36eaa532be298
Merge: 6030e5637f7 30ba0217d1a
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Tue Sep 29 16:05:06 2026 -0400
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
ext/tidy: Reject tidyNode use after the document is reparsed
diff --cc NEWS
index 40eaef9775a,4ffc2b8c0bc..6fe75ef13e9
--- a/NEWS
+++ b/NEWS
@@@ -163,12 -166,22 +163,16 @@@ PH
lookup, and an unchecked CreateFileA() failure. (Ilia Alshanetsky)
. Fix persistent stream context lifetime during shutdown (Levi Morrison)
+ - Tidy:
+ . Fixed a use-after-free when a tidyNode is used after its document is
+ reparsed. (Ilia Alshanetsky)
+
-- XSL:
- . Fixed bug GH-23730 (use-after-free when XSLTProcessor::importStylesheet()
- is called during a transformation). (David Carlier)
-
-- Zip:
- . Fixed ZipArchive::extractTo() ignoring files given in a non-list array.
- (David Carlier)
- . Fixed bug GH-23747 (ZipArchive::close() use-after-free from a progress or
- cancel callback). (David Carlier)
+- Zlib:
+ . Fixed inflate_init() dropping the preset dictionary for raw streams with
+ a non-default window. (Ilia Alshanetsky)
-24 Sep 2026, PHP 8.4.26
+24 Sep 2026, PHP 8.5.11
- BCMath:
. Fixed out-of-bounds read in bc_is_zero_for_scale() when scale exceeds
diff --cc ext/tidy/tidy.c
index b1daeae9d1b,4b9d0cb5d1c..c40439f527f
--- a/ext/tidy/tidy.c
+++ b/ext/tidy/tidy.c
@@@ -67,9 -70,17 +67,14 @@@
#define TIDY_FETCH_ONLY_OBJECT \
PHPTidyObj *obj; \
- TIDY_SET_CONTEXT; \
- if (zend_parse_parameters_none() != SUCCESS) { \
- RETURN_THROWS(); \
- } \
- obj = Z_TIDY_P(object); \
+ ZEND_PARSE_PARAMETERS_NONE(); \
+ obj = Z_TIDY_P(ZEND_THIS); \
+ #define TIDY_FETCH_VALID_NODE \
+ TIDY_FETCH_ONLY_OBJECT; \
+ if (tidy_node_validate(obj) != SUCCESS) { \
+ RETURN_THROWS(); \
+ }
#define TIDY_SET_DEFAULT_CONFIG(_doc) \
if (TG(default_config) && TG(default_config)[0]) { \
php_tidy_load_config(_doc, TG(default_config)); \
@@@ -93,10 -104,11 +98,11 @@@ typedef enum
} tidy_base_nodetypes;
struct _PHPTidyDoc {
- TidyDoc doc;
- TidyBuffer *errbuf;
- unsigned int ref_count;
- size_t parse_generation;
- unsigned int initialized:1;
+ TidyDoc doc;
+ TidyBuffer *errbuf;
+ uint32_t ref_count;
++ size_t parse_generation;
+ bool initialized;
};
struct _PHPTidyObj {
@@@ -215,9 -230,103 +222,24 @@@ static zend_result php_tidy_apply_confi
return SUCCESS;
}
-static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value)
-{
- TidyOption opt = tidyGetOptionByName(doc, optname);
- zend_string *str, *tmp_str;
- zend_long lval;
-
- if (!opt) {
- php_error_docref(NULL, E_WARNING, "Unknown Tidy configuration option \"%s\"", optname);
- return FAILURE;
- }
-
-#if defined(HAVE_TIDYOPTGETCATEGORY)
- if (tidyOptGetCategory(opt) == TidyInternalCategory) {
-#else
- if (tidyOptIsReadOnly(opt)) {
-#endif
- php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname);
- return FAILURE;
- }
-
- switch(tidyOptGetType(opt)) {
- case TidyString:
- str = zval_get_tmp_string(value, &tmp_str);
- if (tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str))) {
- zend_tmp_string_release(tmp_str);
- return SUCCESS;
- }
- zend_tmp_string_release(tmp_str);
- break;
-
- case TidyInteger: /* integer or enum */
- ZVAL_DEREF(value);
- /* Enum will correspond to a non-numeric string or object */
- if (Z_TYPE_P(value) == IS_STRING || Z_TYPE_P(value) == IS_OBJECT) {
- double dval;
- str = zval_try_get_tmp_string(value, &tmp_str);
- if (UNEXPECTED(!str)) {
- return FAILURE;
- }
- uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &lval, &dval, true);
- if (type == IS_DOUBLE) {
- lval = zend_dval_to_lval_cap(dval);
- type = IS_LONG;
- }
- if (type == IS_LONG) {
- if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
- zend_tmp_string_release(tmp_str);
- return SUCCESS;
- }
- } else {
- if (tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str))) {
- zend_tmp_string_release(tmp_str);
- return SUCCESS;
- }
- }
- zend_tmp_string_release(tmp_str);
- } else {
- lval = zval_get_long(value);
- if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
- return SUCCESS;
- }
- }
- break;
-
- case TidyBoolean:
- lval = zval_get_long(value);
- if (tidyOptSetBool(doc, tidyOptGetId(opt), lval)) {
- return SUCCESS;
- }
- break;
-
- default:
- php_error_docref(NULL, E_WARNING, "Unable to determine type of configuration option");
- break;
- }
-
- return FAILURE;
-}
-
+ static zend_result tidy_node_validate(const PHPTidyObj *obj)
+ {
+ if (!obj->ptdoc) {
+ zend_throw_error(NULL, "tidyNode object is not initialized");
+ return FAILURE;
+ }
+
+ if (obj->node_generation != obj->ptdoc->parse_generation) {
+ zend_throw_error(NULL, "tidyNode object is no longer valid after its document was reparsed");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+ }
+
static void tidy_create_node_object(zval *zv, PHPTidyDoc *ptdoc, TidyNode node)
{
- tidy_instantiate(tidy_ce_node, zv);
+ object_init_ex(zv, tidy_ce_node);
PHPTidyObj *newobj = Z_TIDY_P(zv);
newobj->node = node;
newobj->type = is_node;
@@@ -378,7 -488,8 +401,8 @@@ static zend_object *tidy_object_new(zen
intern->ptdoc = emalloc(sizeof(PHPTidyDoc));
intern->ptdoc->doc = tidyCreate();
intern->ptdoc->ref_count = 1;
+ intern->ptdoc->parse_generation = 0;
- intern->ptdoc->initialized = 0;
+ intern->ptdoc->initialized = false;
intern->ptdoc->errbuf = emalloc(sizeof(TidyBuffer));
tidyBufInit(intern->ptdoc->errbuf);
@@@ -813,12 -873,13 +844,13 @@@ static zend_result php_tidy_parse_strin
}
}
- obj->ptdoc->initialized = 1;
+ obj->ptdoc->initialized = true;
tidyBufInit(&buf);
+ obj->ptdoc->parse_generation++;
- tidyBufAttach(&buf, (byte *) string, len);
+ tidyBufAttach(&buf, (byte *) ZSTR_VAL(string), (unsigned int) ZSTR_LEN(string));
if (tidyParseBuffer(obj->ptdoc->doc, &buf) < 0) {
- php_error_docref(NULL, E_WARNING, "%s", obj->ptdoc->errbuf->bp);
+ php_error_docref(NULL, E_WARNING, "%s", (const char*) obj->ptdoc->errbuf->bp);
return FAILURE;
}
tidy_doc_update_properties(obj);
@@@ -1475,27 -1554,39 +1507,27 @@@ PHP_FUNCTION(tidy_get_body
/* {{{ Returns true if this node has children */
PHP_METHOD(tidyNode, hasChildren)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyGetChild(obj->node)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyGetChild(obj->node));
}
/* }}} */
/* {{{ Returns true if this node has siblings */
PHP_METHOD(tidyNode, hasSiblings)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (obj->node && tidyGetNext(obj->node)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(obj->node && tidyGetNext(obj->node));
}
/* }}} */
/* {{{ Returns true if this node represents a comment */
PHP_METHOD(tidyNode, isComment)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyNodeGetType(obj->node) == TidyNode_Comment) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Comment);
}
/* }}} */
@@@ -1518,36 -1609,52 +1550,36 @@@ PHP_METHOD(tidyNode, isHtml
/* {{{ Returns true if this node represents text (no markup) */
PHP_METHOD(tidyNode, isText)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyNodeGetType(obj->node) == TidyNode_Text) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Text);
}
/* }}} */
/* {{{ Returns true if this node is JSTE */
PHP_METHOD(tidyNode, isJste)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyNodeGetType(obj->node) == TidyNode_Jste) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Jste);
}
/* }}} */
/* {{{ Returns true if this node is ASP */
PHP_METHOD(tidyNode, isAsp)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyNodeGetType(obj->node) == TidyNode_Asp) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Asp);
}
/* }}} */
/* {{{ Returns true if this node is PHP */
PHP_METHOD(tidyNode, isPhp)
{
- TIDY_FETCH_ONLY_OBJECT;
+ TIDY_FETCH_VALID_NODE;
- if (tidyNodeGetType(obj->node) == TidyNode_Php) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Php);
}
/* }}} */