Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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

オープン
#24,286 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
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日 16時間
マージ済み PR(30日)
151

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

php/php-src のほかの issue

php/php-src の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。