php / php-src

The PHP Interpreter
https://www.php.net
Other
37.98k stars 7.73k forks source link

Segmentation fault for enabled observers when calling trait method of internal trait when opcache is loaded #13712

Closed TimWolla closed 6 months ago

TimWolla commented 6 months ago

Description

Perform the following steps:

  1. Clone php-src into a fresh directory to ensure it is clean.
  2. ./buildconf
  3. ./configure --enable-debug --enable-zend-test --enable-fpm
  4. make -j$(nproc)

Create the following files:

crasher/fpm.conf

[global]
error_log = /dev/stdout
[unconfined]
listen = 0.0.0.0:9001
pm = static
pm.max_children = 1

catch_workers_output = yes

crasher/php.ini

zend_extension=/path/to/opcache.so
zend_test.observer.enabled=1
zend_test.observer.observe_all=1

crasher/test.php

<?php
class Foo {
    use _ZendTestTrait;
}

$f = new Foo();
$f->testMethod();

Run sapi/fpm/php-fpm -y crasher/fpm.conf -c crasher/ -F

And then send a request to FPM. I'm using: https://github.com/akerouanton/fcgi-client via fcgi-client get 127.0.0.1:9001 crasher/test.php

Now observe the the FPM worker dies:

[14-Mar-2024 17:17:25] WARNING: [pool unconfined] child 922211 exited on signal 11 (SIGSEGV - core dumped) after 58.806585 seconds from start
[14-Mar-2024 17:17:25] NOTICE: [pool unconfined] child 922666 started

Running in gdb:

follow-exec-mode  follow-fork-mode  
(gdb) set follow-fork-mode child
(gdb) run -y crasher/fpm.conf -c crasher/ -F
Starting program: /tmp/php-src/sapi/fpm/php-fpm -y crasher/fpm.conf -c crasher/ -F

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[14-Mar-2024 17:18:18] NOTICE: fpm is running, pid 922740
[Attaching after Thread 0x7ffff7afad80 (LWP 922740) fork to child process 922744]
[New inferior 2 (process 922744)]
[Detaching after fork from parent process 922740]
[Inferior 1 (process 922740) detached]
[14-Mar-2024 17:18:18] NOTICE: ready to handle connections
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Thread 2.1 "php-fpm" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7afad80 (LWP 922744)]
0x0000555555c5167e in _zend_observe_fcall_begin (execute_data=0x7ffff50170a0) at /tmp/php-src/Zend/zend_observer.c:227
227     if (!*handler) {
(gdb) bt
#0  0x0000555555c5167e in _zend_observe_fcall_begin (execute_data=0x7ffff50170a0) at /tmp/php-src/Zend/zend_observer.c:227
#1  0x0000555555c51788 in zend_observer_fcall_begin (execute_data=0x7ffff50170a0) at /tmp/php-src/Zend/zend_observer.c:257
#2  0x0000555555b850ca in ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER () at /tmp/php-src/Zend/zend_vm_execute.h:2095
#3  0x0000555555c01e6d in execute_ex (ex=0x7ffff5017020) at /tmp/php-src/Zend/zend_vm_execute.h:57419
#4  0x0000555555c0771b in zend_execute (op_array=0x7ffff5080000, return_value=0x0) at /tmp/php-src/Zend/zend_vm_execute.h:62776
#5  0x0000555555b3cde2 in zend_execute_script (type=8, retval=0x0, file_handle=0x7fffffffdab0) at /tmp/php-src/Zend/zend.c:1888
#6  0x0000555555a88d13 in php_execute_script_ex (primary_file=0x7fffffffdab0, retval=0x0) at /tmp/php-src/main/main.c:2507
#7  0x0000555555a88e99 in php_execute_script (primary_file=0x7fffffffdab0) at /tmp/php-src/main/main.c:2547
#8  0x0000555555cdf301 in main (argc=6, argv=0x7fffffffdf58) at /tmp/php-src/sapi/fpm/fpm/fpm_main.c:1937
(gdb) bt full
#0  0x0000555555c5167e in _zend_observe_fcall_begin (execute_data=0x7ffff50170a0) at /tmp/php-src/Zend/zend_observer.c:227
        function = 0x55554dcbed28
        handler = 0x0
        possible_handlers_end = 0x7ffff5017080
        end_handler = 0x60f5078000
#1  0x0000555555c51788 in zend_observer_fcall_begin (execute_data=0x7ffff50170a0) at /tmp/php-src/Zend/zend_observer.c:257
No locals.
#2  0x0000555555b850ca in ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER () at /tmp/php-src/Zend/zend_vm_execute.h:2095
        retval = {value = {lval = 140737303900272, dval = 6.9533466945443672e-310, counted = 0x7ffff5017070, str = 0x7ffff5017070, arr = 0x7ffff5017070, obj = 0x7ffff5017070, res = 0x7ffff5017070, ref = 0x7ffff5017070, ast = 0x7ffff5017070, zv = 0x7ffff5017070, ptr = 0x7ffff5017070, ce = 0x7ffff5017070, func = 0x7ffff5017070, ww = {w1 = 4110512240, w2 = 32767}}, u1 = {
            type_info = 1, v = {type = 1 '\001', type_flags = 0 '\000', u = {extra = 0}}}, u2 = {next = 32767, cache_slot = 32767, opline_num = 32767, lineno = 32767, num_args = 32767, fe_pos = 32767, fe_iter_idx = 32767, guard = 32767, constant_flags = 32767, extra = 32767}}
        should_throw = false
        call = 0x7ffff50170a0
        fbc = 0x55554dcbed28
        ret = 0x7fffffffc3c0
        __PRETTY_FUNCTION__ = "ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER"
#3  0x0000555555c01e6d in execute_ex (ex=0x7ffff5017020) at /tmp/php-src/Zend/zend_vm_execute.h:57419
        vm_stack_data = {hybrid_jit_red_zone = "\000\306\377\377\377\177\000\000 p\001\365\377\177\000\000 \306\377\377\377\177\000\000\377\026\305UUU\000\000\240)\233VUU\000\000 p\001\365\377\177\000", orig_opline = 0x7ffff7ffd000 <_rtld_global>, orig_execute_data = 0x555556d05e38}
        __PRETTY_FUNCTION__ = "execute_ex"
#4  0x0000555555c0771b in zend_execute (op_array=0x7ffff5080000, return_value=0x0) at /tmp/php-src/Zend/zend_vm_execute.h:62776
        execute_data = 0x7ffff5017020
        object_or_called_scope = 0x0
        call_info = 1245184
#5  0x0000555555b3cde2 in zend_execute_script (type=8, retval=0x0, file_handle=0x7fffffffdab0) at /tmp/php-src/Zend/zend.c:1888
        op_array = 0x7ffff5080000
        ret = SUCCESS
#6  0x0000555555a88d13 in php_execute_script_ex (primary_file=0x7fffffffdab0, retval=0x0) at /tmp/php-src/main/main.c:2507
        realfile = "\200\311\377\377\377\177\000\000p\312\377\377\377\177\000\0000\312\377\377.\000\000\000D,\232VUU\000\0000\330\377\377\377\177\000\000\204\232\256UUU\000\000\240o\006WUU\000\000\340\311\377\377\376\377\377\377\000\000\000\000\002\000\000\000\000\000\000\000\002", '\000' <repeats 19 times>, "\001\000\000\000\322\377\377\377\340\326\377\377\377\177\000\000\200\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000`\327\377\377\377\177\000\000`\327\377\377\377\177\000\000`\312\377\377\377\177\000\000`\312\377\377\377\177\000\000\001\000\000\000\b\000\000\000@\023\aWUU\000\000\200\023\aWUU\000\000\000\000\000\000\000\000\000\000\220n\006WUU\000\000"...
        __orig_bailout = 0x7fffffffdbd0
        __bailout = {{__jmpbuf = {140737488346968, 4076861433373691849, 0, 140737488347024, 93825017077304, 140737354125312, 4076861433428217801, 7910220288533630921}, __mask_was_saved = 0, __saved_mask = {__val = {93824999389906, 93825020774240, 93825018660848, 140737488341056, 0, 1501199897131959, 1, 140737488341104, 93825019743084, 7307217257776754478, 8083526420148793202, 
                140737471606888, 93825000083778, 140737354125312, 1, 93825013716635}}}}
        prepend_file_p = 0x0
        append_file_p = 0x0
        prepend_file = {handle = {fp = 0x7ffff5003040, stream = {handle = 0x7ffff5003040, isatty = -184094336, reader = 0x7fffffffd860, fsizer = 0x555555a9074b <php_resolve_path+1561>, closer = 0x0}}, filename = 0x555556ec23f8, opened_path = 0x10, type = 104 'h', primary_script = 224, in_list = 6, buf = 0x7fffffffc790 "\320\307\377\377\377\177", len = 4150541271}
        append_file = {handle = {fp = 0x7fffffffc850, stream = {handle = 0x7fffffffc850, isatty = 1458316282, reader = 0x555556ec23f9, fsizer = 0x7ffff506e0a0, closer = 0x7fffffffc7d0}}, filename = 0x7ffff506f180, opened_path = 0x555556ec23f9, type = 88 'X', primary_script = 223, in_list = 255, buf = 0xc <error: Cannot access memory at address 0xc>, len = 140737488340960}
        old_cwd = 0x7ffff5075000 "/tmp/php-src"
        result = true
#7  0x0000555555a88e99 in php_execute_script (primary_file=0x7fffffffdab0) at /tmp/php-src/main/main.c:2547
No locals.
#8  0x0000555555cdf301 in main (argc=6, argv=0x7fffffffdf58) at /tmp/php-src/sapi/fpm/fpm/fpm_main.c:1937
        primary_script = 0x7ffff5004070 "crasher/test.php"
        __orig_bailout = 0x0
        __bailout = {{__jmpbuf = {140737488346968, 4076861433140907977, 0, 140737488347024, 93825017077304, 140737354125312, 4076861433379983305, 7910220609485180873}, __mask_was_saved = 0, __saved_mask = {__val = {0, 0, 1000, 256, 1000, 0, 0, 140737308402544, 140737308402584, 140737308402448, 140737308402488, 140737308402448, 140737308402488, 140737308402584, 0, 
                140737308402544}}}}
        exit_status = 0
        cgi = 0
        c = -1
--Type <RET> for more, q to quit, c to continue without paging--
        use_extended_info = 0
        file_handle = {handle = {fp = 0x7ffff5002000, stream = {handle = 0x7ffff5002000, isatty = 0, reader = 0x555555aa8bdc <_php_stream_read>, fsizer = 0x555555a870d6 <php_zend_stream_fsizer>, closer = 0x555555a870b2 <php_zend_stream_closer>}}, filename = 0x7ffff506e050, opened_path = 0x7ffff506f1e0, type = 2 '\002', primary_script = false, in_list = true, 
          buf = 0x7ffff505b0a0 "<?php\nclass Foo {\n\tuse _ZendTestTrait;\n}\n\n$f = new Foo();\n$f->testMethod();\n", len = 76}
        orig_optind = 1
        orig_optarg = 0x0
        ini_builder = {value = 0x0, length = 0}
        max_requests = 0
        requests = 0
        fcgi_fd = 10
        request = 0x55555708ef00
        fpm_config = 0x7fffffffe2de ""
        fpm_prefix = 0x0
        fpm_pid = 0x0
        test_conf = 0
        force_daemon = 0
        force_stderr = 0
        php_information = 0
        php_allow_to_run_as_root = 0
        __func__ = "main"
        ret = FPM_INIT_CONTINUE

PHP Version

git master

Operating System

Ubuntu 23.10

iluuu1994 commented 6 months ago

/cc @bwoebi

bwoebi commented 6 months ago

I can verify this, ./sapi/cli/php -d zend_extension=$(pwd)/modules/opcache.so -d opcache.enable_cli=1 -d zend_test.observer.enabled=1 reproducer.php is enough.

https://github.com/php/php-src/blob/bf4311325c408d8112a4a734fe399946aa99e65d/ext/opcache/zend_persist.c#L731-L733

I suppose the check here is not good enough to cover traits. I'm not sure how to recognize a trait inherited method here. For reasons ZEND_ACC_TRAIT_CLONE is not applied to internal functions. Is there a reason? Otherwise I'm going to propose a PR using that.