Commit d19c413d485 for php.net

commit d19c413d48507e0675df64c7c9a667f8fa7332d1
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Sep 21 17:22:01 2026 -0400

    Zend: release the trampoline when zend_call_function() bails early

    Two early returns in zend_call_function() skip
    zend_release_fcall_info_cache(): the one taken when zend_deprecated_function()
    leaves an exception pending, and the one taken when zend_handle_undef_args()
    fails. Both leak the trampoline, so a #[\Deprecated] __call() whose
    deprecation is promoted to an exception, and a missing required argument on
    Closure::__invoke(), each leak 256 bytes per call and trip the
    EG(trampoline) assertion in shutdown_executor() on a debug build.

    Closes GH-23831

diff --git a/Zend/tests/call_function_trampoline_leak.phpt b/Zend/tests/call_function_trampoline_leak.phpt
new file mode 100644
index 00000000000..bdf8f257d95
--- /dev/null
+++ b/Zend/tests/call_function_trampoline_leak.phpt
@@ -0,0 +1,36 @@
+--TEST--
+zend_call_function() must release the trampoline when it bails before the call
+--FILE--
+<?php
+
+class A {
+    #[\Deprecated]
+    public function __call($func, $args) {
+    }
+}
+
+set_error_handler(function ($errno, $errstr) {
+    throw new Exception($errstr);
+});
+
+// Indirect call so that the callable is resolved by zend_call_function()
+// rather than by the ZEND_INIT_USER_CALL handler, which frees the trampoline
+// itself.
+$call = 'call_user_func_array';
+
+try {
+    $call([new A(), 'foo'], []);
+} catch (Exception $e) {
+    echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+try {
+    $call([Closure::fromCallable('strcmp'), '__invoke'], ['string2' => 'b']);
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Exception: Method A::foo() is deprecated
+ArgumentCountError: Closure::__invoke(): Argument #1 ($string1) not passed
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 6e42be5f888..9ccb36a1e15 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -853,6 +853,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_

 		if (UNEXPECTED(EG(exception))) {
 			zend_vm_stack_free_call_frame(call);
+			zend_release_fcall_info_cache(fci_cache);
 			return SUCCESS;
 		}
 	}
@@ -969,6 +970,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
 		if (zend_handle_undef_args(call) == FAILURE) {
 			zend_vm_stack_free_args(call);
 			zend_vm_stack_free_call_frame(call);
+			zend_release_fcall_info_cache(fci_cache);
 			return SUCCESS;
 		}
 	}