php / php/php-src

Preloading anonymous class inheriting from autoloaded unlinked class leads to return type error

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

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

Bug Status: Needs Triage
主要言語
C
スター
40.4k
フォーク
8.1k
平均マージ
2日 13時間
マージ済み PR(30日)
96

説明

Description
Issue

When using PHP-FPM that is, and only starting when the respective code is used the second time.

The following code:

// preload.php
<?php
require __DIR__ . '/Main.php';

// index.php
<?php
require __DIR__ . '/Main.php';
spl_autoload_register(function ($class) {
    if ($class === 'SomeType') require __DIR__ . '/SomeType.php';
});

var_dump((new Main)->create());

// Main.php
<?php
class Main {
    function create(): SomeType {
        return new class extends SomeType {};
    }
}

// SomeType.php
<?php
class SomeType {}
# php.ini
opcache.enable=1
opcache.optimization_level=-1
opcache.preload=/var/www/html/preload.php
opcache.preload_user=www-data

Where index.php is called twice

Resulted in this output:

# During preload
Warning: Can't preload unlinked class SomeType@anonymous: Unknown parent SomeType in /var/www/html/Main.php on line 4

# Call 1
object(SomeType@anonymous)#3 (0) { }

# Call 2
Fatal error: Uncaught TypeError: Main::create(): Return value must be of type SomeType, SomeType@anonymous returned in /var/www/html/Main.php:4
Stack trace:
#0 /var/www/html/index.php(7): Main->create()
#1 {main}
  thrown in /var/www/html/Main.php on line 4

But I expected this output instead:

# During preload
Warning: Can't preload unlinked class SomeType@anonymous: Unknown parent SomeType in /var/www/html/Main.php on line 4

# Call 1
object(SomeType@anonymous)#3 (0) { }

# Call 2
object(SomeType@anonymous)#3 (0) { }

Reproducing

This can be reproduced by additionally configuring these files

# php-fpm-www.conf
[www]
user=www-data
group=www-data
listen=127.0.0.1:9000
pm=static
pm.max_children=1
# nginx.conf
events {
    worker_connections  4096;
}
http {
    server {
        location ~ \.php$ {
            fastcgi_pass php-test:9000;
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
            include fastcgi_params;
        }
    }
}

and then running

docker run --rm -it --name php-test -v $(pwd):/var/www/html -v $(pwd)/php-fpm-www.conf:/usr/local/etc/php-fpm.d/www.conf -v $(pwd)/php.ini:/usr/local/etc/php/conf.d/php.ini php:8.1.13-fpm-bullseye bash -c "docker-php-ext-install opcache && php-fpm"
# separately / detached
docker run --rm -it -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 --link php-test nginx:1.23.3
curl http://localhost:8080/index.php
curl http://localhost:8080/index.php

Segfaults

Under certain circumstances, this leads to segfaults, but I haven't been able to setup a sharable reproducible case.
Occasionally between segfaults I get errors like these:

  • Uncaught exception: Class "" not found
  • Uncaught exception: Class "H?" not found

Backtrace from such a segfault:

Core was generated by `php-fpm: pool www      '.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005564d172fe35 in zend_lookup_class_ex (name=0x800000017c9e55d4, key=0x42973350, flags=2560) at /usr/src/php/Zend/zend_execute_API.c:1050
1050    /usr/src/php/Zend/zend_execute_API.c: No such file or directory.
[Current thread is 1 (Thread 0x7f8a9adbe980 (LWP 263))]
(gdb) bt
#0  0x00005564d172fe35 in zend_lookup_class_ex (name=0x800000017c9e55d4, key=0x42973350, flags=2560) at /usr/src/php/Zend/zend_execute_API.c:1050
#1  0x00005564d1731092 in zend_fetch_class_by_name (class_name=0x800000017c9e55d4, key=0x42973350, fetch_type=2560) at /usr/src/php/Zend/zend_execute_API.c:1601
#2  0x00005564d184233d in zend_do_link_class (ce=0x7f8a96404338, lc_parent_name=0x42973350, key=0x42973000) at /usr/src/php/Zend/zend_inheritance.c:2715
#3  0x00005564d178bae1 in ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER () at /usr/src/php/Zend/zend_vm_execute.h:2955
#4  0x00005564d17fcd2c in execute_ex (ex=0x7f8a9aa14020) at /usr/src/php/Zend/zend_vm_execute.h:55863
#5  0x00005564d18014fa in zend_execute (op_array=0x7f8a9aa75140, return_value=0x0) at /usr/src/php/Zend/zend_vm_execute.h:60147
#6  0x00005564d17488dc in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /usr/src/php/Zend/zend.c:1799
#7  0x00005564d169e08b in php_execute_script (primary_file=0x7fff379ad520) at /usr/src/php/main/main.c:2541
#8  0x00005564d18cf2d5 in main (argc=1, argv=0x7fff379ad9a8) at /usr/src/php/sapi/fpm/fpm/fpm_main.c:1914
PHP Version

8.1.13

Operating System

No response

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

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

調査の方向性

提供された Docker 設定を使って、Main.php、SomeType.php、preload.php による PHP-FPM の preload ケースを再現し、その後 zend_inheritance.c、zend_execute_API.c、および zend_vm_execute.h の匿名クラスハンドラーを調査します。2 回目のリクエストを追跡し、宣言された戻り値の型、繰り返し実行、および TypeError やクラッシュを伴わない、報告された preload 警告を対象とするリグレッションテストを追加します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
php
領域
backend
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

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

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