Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

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

未关闭
#24,286 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

@kairosci 已经在做这个了。

开始于 2026年10月25日。

  • #24700 来自 @kairosci —— 未关闭

评估

难度
5/5
预计耗时
一周以上
新手友好度
8/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
c, php
领域
compilers

调研方向

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.

由索引模型根据 Issue 内容生成。

描述

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

主要语言
C
星标
40.7k
派生
8.3k
平均合并
2 天 20 小时
30 天内合并 PR
147

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

php/php-src 的其他 Issue

查看 php/php-src 的全部 Issue

相似的 Issue

更多 C Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。