diff --git a/UPGRADING b/UPGRADING
index b7b160820bb2a..e55b9730f71a4 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -246,6 +246,7 @@ PHP 8.6 UPGRADE NOTES
creation of intermediate Closures, the overhead of calling userland
callbacks from internal functions and providing for better insight for the
JIT.
+ . The performance of the TAILCALL VM has been improved.
- DOM:
. Made splitText() faster and consume less memory.
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index cbfae90802cfa..7dfedca98d3b9 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -53457,6 +53457,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER(
# undef ZEND_VM_RETURN
# undef ZEND_VM_DISPATCH_TO_HELPER
# undef ZEND_VM_INTERRUPT
+# undef ZEND_VM_ENTER_EX
+# undef ZEND_VM_LEAVE
# define ZEND_VM_TAIL_CALL(call) ZEND_MUSTTAIL return call
# define ZEND_VM_CONTINUE() ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))
@@ -53468,6 +53470,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER(
} while (0)
# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()
# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))
+# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()
+# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index f8989b2336f47..ed0256832d589 100755
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -2126,6 +2126,8 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"# undef ZEND_VM_RETURN\n");
out($f,"# undef ZEND_VM_DISPATCH_TO_HELPER\n");
out($f,"# undef ZEND_VM_INTERRUPT\n");
+ out($f,"# undef ZEND_VM_ENTER_EX\n");
+ out($f,"# undef ZEND_VM_LEAVE\n");
out($f,"\n");
out($f,"# define ZEND_VM_TAIL_CALL(call) ZEND_MUSTTAIL return call\n");
out($f,"# define ZEND_VM_CONTINUE() ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n");
@@ -2137,6 +2139,8 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f," } while (0)\n");
out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n");
+ out($f,"# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
+ out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n");
out($f,"\n");
out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n");
out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n");
diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c
index c5467396d4bfe..6221c7f7a6e71 100644
--- a/ext/dba/libinifile/inifile.c
+++ b/ext/dba/libinifile/inifile.c
@@ -249,7 +249,7 @@ val_type inifile_fetch(inifile *dba, const key_type *key, int skip) {
ln.key.group = estrdup(dba->next.key.group);
} else {
/* specific instance or not same key -> restart search */
- /* the slow way: restart and seacrch */
+ /* the slow way: restart and search */
php_stream_rewind(dba->fp);
inifile_line_free(&dba->next);
}
@@ -471,7 +471,7 @@ static int inifile_delete_replace_append(inifile *dba, const key_type *key, cons
* 8) Append temporary stream
*/
- assert(!append || (key->name && value)); /* missuse */
+ assert(!append || (key->name && value)); /* misuse */
/* 1 - 3 */
inifile_find_group(dba, key, &pos_grp_start);
diff --git a/ext/xsl/tests/gh21496.phpt b/ext/xsl/tests/gh21496.phpt
new file mode 100644
index 0000000000000..7a5cea37937d0
--- /dev/null
+++ b/ext/xsl/tests/gh21496.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-21496 (UAF in dom_objects_free_storage when importing non-document node as stylesheet)
+--EXTENSIONS--
+dom
+xsl
+--CREDITS--
+YuanchengJiang
+--FILE--
+loadXML(<<
+XML);
+$doc->documentElement->appendChild($comment);
+unset($doc);
+$proc = new XSLTProcessor();
+var_dump($proc->importStylesheet($comment));
+$sxe = simplexml_load_string('');
+$proc = new XSLTProcessor();
+$proc->importStylesheet($sxe);
+?>
+--EXPECTF--
+Warning: XSLTProcessor::importStylesheet(): compilation error: file %s line 1 element container in %s on line %d
+
+Warning: XSLTProcessor::importStylesheet(): xsltParseStylesheetProcess : document is not a stylesheet in %s on line %d
+bool(false)
+
+Warning: XSLTProcessor::importStylesheet(): compilation error: element container in %s on line %d
+
+Warning: XSLTProcessor::importStylesheet(): xsltParseStylesheetProcess : document is not a stylesheet in %s on line %d
+
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
index 823c56f1cfb57..d06d008176172 100644
--- a/ext/xsl/xsltprocessor.c
+++ b/ext/xsl/xsltprocessor.c
@@ -167,7 +167,7 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
xsltStylesheetPtr sheetp;
bool clone_docu = false;
xmlNode *nodep = NULL;
- zval *cloneDocu, rv, clone_zv;
+ zval *cloneDocu, rv, clone_zv, owner_zv;
zend_string *member;
id = ZEND_THIS;
@@ -175,10 +175,40 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
RETURN_THROWS();
}
+ nodep = php_libxml_import_node(docp);
+ if (nodep == NULL) {
+ zend_argument_type_error(1, "must be a valid XML node");
+ RETURN_THROWS();
+ }
+
+ if (Z_OBJ_HANDLER_P(docp, clone_obj) == NULL) {
+ zend_argument_type_error(1, "must be a cloneable node");
+ RETURN_THROWS();
+ }
+
+ ZVAL_UNDEF(&owner_zv);
+
+ /* For non-document nodes, resolve the ownerDocument and clone that
+ * instead as xsltParseStylesheetProcess may free nodes in the document. */
+ if (nodep->type != XML_DOCUMENT_NODE && nodep->type != XML_HTML_DOCUMENT_NODE) {
+ if (nodep->doc == NULL) {
+ zend_argument_value_error(1, "must be part of a document");
+ RETURN_THROWS();
+ }
+
+ /* See dom_import_simplexml_common */
+
+ dom_object *nodeobj = (dom_object *) ((char *) Z_OBJ_P(docp) - Z_OBJ_HT_P(docp)->offset);
+
+ php_dom_create_object((xmlNodePtr) nodep->doc, &owner_zv, nodeobj);
+ docp = &owner_zv;
+ }
+
/* libxslt uses _private, so we must copy the imported
* stylesheet document otherwise the node proxies will be a mess.
* We will clone the object and detach the libxml internals later. */
zend_object *clone = Z_OBJ_HANDLER_P(docp, clone_obj)(Z_OBJ_P(docp));
+ zval_ptr_dtor(&owner_zv);
if (!clone) {
RETURN_THROWS();
}