Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở
#24,286 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@kairosci đang làm issue này rồi.

Từ ngày 25/10/2026.

  • #24700 của @kairosci — đang mở

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
8/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c, php
Lĩnh vực
compilers

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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

Ngôn ngữ chính
C
Star
40.7k
Fork
8.3k
Merge trung bình
2 ngày 17 giờ
Pull request đã merge (30 ngày)
159

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của php/php-src

Tất cả issue của php/php-src

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.