Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
TimWolla opened this issue Mar 14, 2024 · 2 comments

Comments

@TimWolla
Copy link
Member

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
Copy link
Member

/cc @bwoebi

@bwoebi
Copy link
Member

bwoebi commented Mar 16, 2024

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.

// Real dynamically created internal functions like enum methods must have their own run_time_cache pointer. They're always on the same scope as their defining class.
// However, copies - as caused by inheritance of internal methods - must retain the original run_time_cache pointer, shared with the source function.
if (!op_array->scope || op_array->scope == ce) {

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.

@bwoebi bwoebi changed the title Segmentation fault for enabled observers when calling trait method of internal trait when opcache is loaded in FPM Segmentation fault for enabled observers when calling trait method of internal trait when opcache is loaded Mar 17, 2024
bwoebi added a commit to bwoebi/php-src that referenced this issue Mar 17, 2024
…g trait method of internal trait when opcache is loaded

Inherited methods regardless of source must share the original runtime cache. Traits were missed.
This adds ZEND_ACC_TRAIT_CLONE to internal functions as well to allow easy distinction of these.

On top of that, also fixing the minimum base of the shared opcache memory to 0x4000 to avoid a possible 0x0 base, which may cause all sorts of segfaults. (Breaks WSL 1.)
bwoebi added a commit to bwoebi/php-src that referenced this issue Mar 17, 2024
…g trait method of internal trait when opcache is loaded

Inherited methods regardless of source must share the original runtime cache. Traits were missed.
This adds ZEND_ACC_TRAIT_CLONE to internal functions as well to allow easy distinction of these.

On top of that, also fixing the minimum base of the shared opcache memory to 0x4000 to avoid a possible 0x0 base, which may cause all sorts of segfaults. (Breaks WSL 1.)
bwoebi added a commit to bwoebi/php-src that referenced this issue Mar 17, 2024
…g trait method of internal trait when opcache is loaded

Inherited methods regardless of source must share the original runtime cache. Traits were missed.
This adds ZEND_ACC_TRAIT_CLONE to internal functions as well to allow easy distinction of these.

On top of that, also fixing the minimum base of the shared opcache memory to the second huge page to avoid a possible 0x0 base, which may cause all sorts of segfaults. (Breaks WSL 1.)
bwoebi added a commit to bwoebi/php-src that referenced this issue Mar 18, 2024
…g trait method of internal trait when opcache is loaded

Inherited methods regardless of source must share the original runtime cache. Traits were missed.
This adds ZEND_ACC_TRAIT_CLONE to internal functions as well to allow easy distinction of these.

On top of that, also fixing the minimum base of the shared opcache memory to the second huge page to avoid a possible 0x0 base, which may cause all sorts of segfaults. (Breaks WSL 1.)
@bwoebi bwoebi closed this as completed in 10d912d Mar 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants