[php-src] Issue #24286: Tracing JIT loses the result of a dynamic method call traced with Closure::__invoke (8.5 regression)
| From: | liquidice | Date: | Tue, 13 Oct 2026 03:02:39 +0000 |
| Subject: | [php-src] Issue #24286: Tracing JIT loses the result of a dynamic method call traced with Closure::__invoke (8.5 regression) | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-252982@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24286
Author: liquidice
### Description
> With the tracing JIT, the result of a dynamic method call
>
$o->{$m}() is lost when the call site was first traced while
> calling Closure::__invoke. The caller then reads whatever is left in
> the result slot.
The following code:
```php
<?php
class A { function m() { return false; } }
function call($o, $m) { return $o->{$m}(); }
function every($items, $m) {
foreach ($items as $i) { if (!call($i, $m)) return false; }
return true;
}
$c = function () { return false; };
for ($i = 0; $i < 2000; $i++) every([$c], '__invoke');
var_dump(call(new A, 'm'));
```
```sh
php -n -d opcache.enable_cli=1 -d opcache.jit_buffer_size=64M -d opcache.jit=tracing test.php
```
Resulted in this output:
```
UNKNOWN:0
```
But I expected this output instead:
```
bool(false)
```
> - Wrong on 8.5.0, 8.5.11 and PHP-8.5 @ bbfdd69f, with Ò':¦P<Ä
> !þA’Àopcache.jit=tracing and with 1054.
> - Correct with opcache.jit=function, with the JIT off, and on
> 8.4.26 and 8.3.35 (tracing).
> - Not fixed by #23611 (GH-23572).
> - The warm-up through Closure::__invoke is required: warming up
> call() with a plain object's method does not reproduce it. The
> early return false in every() is
> also required.
> Valgrind on PHP-8.5 @ bbfdd69f (-g -O2,
> USE_ZEND_ALLOC=0 valgrind --smc-check=all --track-origins=yes).
> Leaving out the ir_sparse_set_in reports from the JIT compiler,
> every remaining error is about the same never-written VM stack memory: it is used in JIT code,
> printed by var_dump(), then destroyed:
```
Conditional jump or move depends on uninitialised value(s)
at 0x11800ADF: ???
by 0x8C4E9F: zend_execute (zend_vm_execute.h:122008)
by 0x9270D5: zend_execute_script (zend.c:1985)
Uninitialised value was created by a heap allocation
by 0x88CAE8: zend_vm_stack_new_page (zend_execute.h:327)
by 0x88CAE8: zend_vm_stack_init (zend_execute.c:195)
Conditional jump or move depends on uninitialised value(s)
at 0x7668EE: php_var_dump (var.c:130)
by 0x7672E0: zif_var_dump (var.c:257)
by 0x8C0273: ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:1360)
Uninitialised value was created by a heap allocation
by 0x88CAE8: zend_vm_stack_new_page (zend_execute.h:327)
by 0x88CAE8: zend_vm_stack_init (zend_execute.c:195)
Conditional jump or move depends on uninitialised value(s)
at 0x8C02AE: zval_ptr_dtor_nogc (zend_variables.h:35)
by 0x8C02AE: zend_vm_stack_free_args (zend_execute.h:405)
by 0x8C02AE: ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:1379)
Uninitialised value was created by a heap allocation
by 0x88CAE8: zend_vm_stack_new_page (zend_execute.h:327)
by 0x88CAE8: zend_vm_stack_init (zend_execute.c:195)
```
> Real-world impact: Laravel's HigherOrderCollectionProxy
> closure has this shape. Telescope calls it with Ží²Þþ
> 77‹”J¾À˜->every->__invoke($entry) for its filters, and then with
> ->partition->isException() when storing entries. On a fresh
> laravel/laravel 13.35 +
> laravel/telescope 5.25 install (official
> php:8.5.11-cli image, default JIT settings apart from
> opcache.jit=tracing), an artisan command that logs, uses the cache
> and runs queries in a loop crashes with zend_mm_heap corrupted or
> SIGSEGV with APP_ENV=production. It crashed in 28 of 32 runs from
> 250 iterations up. It does not crash with function JIT, with the JIT off, or on 8.4.26.
### PHP Version
```
PHP 8.5.11 (cli) (built: Oct 6 2026 01:22:31) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.5.11, Copyright (c) Zend Technologies
with Zend OPcache v8.5.11, Copyright (c), by Zend Technologies
```
### Operating System
Debian 13 (official Docker image), x86_64