Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Tracing JIT loses the result of a dynamic method call traced with Closure::__invoke (8.5 regression)

Abierto
#24,286 3 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@kairosci ya está trabajando en esto.

Desde el 25/10/2026.

  • #24700 de @kairosci — abierto

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
8/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
c, php
Área
compilers

Línea de trabajo

The payload points at the tracing JIT path around zend_execute in zend_vm_execute.h and the call return handling in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER, with the valgrind traces naming zend_vm_stack_new_page as where the never-written memory comes from. Start by reproducing with the opcache.jit=tracing command in the issue, then compare against opcache.jit=function. Done would mean the result slot is written before it is read and the output is bool(false), with valgrind clean outside the JIT compiler.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Bug Category: JIT Extension: opcache Status: Verified
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
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'));
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 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 ->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

Lenguaje dominante
C
Estrellas
40.7k
Forks
8.3k
Merge medio
2 d 20 h
PR fusionados (30 d)
147

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de php/php-src

Todos los issues de php/php-src

Issues similares

Más issues de C

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.