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);
  }
  /* }}} */