php / php/php-src

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

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

Chưa có ai nhận issue này.

Bug Status: Needs Triage
Ngôn ngữ chính
C
Star
40.4k
Fork
8.1k
Merge trung bình
2 ngày 13 giờ
Pull request đã merge (30 ngày)
96

Mô tả

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

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

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.

Hướng nghiên cứu

Tái hiện trường hợp preload của PHP-FPM với Main.php, SomeType.php, preload.php và cấu hình Docker được cung cấp, sau đó kiểm tra zend_inheritance.c, zend_execute_API.c và trình xử lý lớp ẩn danh trong zend_vm_execute.h. Theo dõi request thứ hai và thêm một bài kiểm thử hồi quy bao quát kiểu trả về được khai báo, việc thực thi lặp lại và cảnh báo preload được báo cáo mà không có TypeError hoặc crash.

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

Đánh giá

Công nghệ
php
Lĩnh vực
backend
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

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.