-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
PHP-FPM segfaults with Opcache enabled with Late Static Binding #9396
Comments
My further investigation into the issue for now(After expanding the EX and CACHED_PTR macros), points to use after free like issue with (execute_data)->run_time_cache storing a no more valid address: (gdb) print ((execute_data)->run_time_cache) Other found facts: |
I arranged to make small testcase which allows to reproduce part of it with opcache.protect_memory=1. @cmb69 , I hope you'll be able to reproduce the crash with it without extra php.ini changes(Except of mentioned one). |
I can reproduce with https://github.com/php/php-src/files/9763190/testConstOptimizerBug.zip and The commit bd98d84
(gdb) run
Starting program: /path/to/php-8.0.26-debug-install/bin/php -d zend_extension=opcache -d opcache.protect_memory=1 -d opcache.enable_cli=1 -d opcache.enable=1 testConstOptimizerBug.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x0000555555974338 in init_func_run_time_cache_i (op_array=0x408bf010) at /path/to/php-src/Zend/zend_execute.c:3678
3678 ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache);
(gdb) bt
#0 0x0000555555974338 in init_func_run_time_cache_i (op_array=0x408bf010) at /path/to/php-src/Zend/zend_execute.c:3678
#1 0x000055555597435a in init_func_run_time_cache (op_array=0x408bf010) at /path/to/php-src/Zend/zend_execute.c:3684
#2 0x0000555555987328 in ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER () at /path/to/php-src/Zend/zend_vm_execute.h:6671
#3 0x00005555559ed18c in execute_ex (ex=0x7ffff7a14020) at /path/to/php-src/Zend/zend_vm_execute.h:55756
#4 0x00005555559f1b7b in zend_execute (op_array=0x7ffff7a5d3c0, return_value=0x0) at /path/to/php-src/Zend/zend_vm_execute.h:59523
#5 0x0000555555940502 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /path/to/php-src/Zend/zend.c:1694
#6 0x00005555558a13d0 in php_execute_script (primary_file=0x7fffffffc790) at /path/to/php-src/main/main.c:2545
#7 0x0000555555a32cb0 in do_cli (argc=10, argv=0x555556221b20) at /path/to/php-src/sapi/cli/php_cli.c:949
#8 0x0000555555a33d0c in main (argc=10, argv=0x555556221b20) at /path/to/php-src/sapi/cli/php_cli.c:1337
(gdb) print op_array->run_time_cache__ptr
$5 = (void ***) 0x408bf0f8
(gdb) set {int}0x408bf0f8=0
Cannot access memory at address 0x408bf0f8
(gdb) print *(op_array->run_time_cache__ptr)
$6 = (void **) 0x0 https://www.php.net/supported-versions.php php 8.0 has active bug fix support until |
Right. While PHP 8.0.26 will be a regular bug fix release, 8.0.26RC1 is supposed to be tagged today, so this is likely to late for this issue to be fixed. I suggest to close the ticket as WONTFIX; those who are affected by this issue should better update to PHP 8.1. |
https://www.npopov.com/2021/10/13/How-opcache-works.html#map-pointers I also see that in php 8.1, this example is pointing into immutable memory through an offset
For mutable memory: map_ptr & 1 == 0
map pointer ----> indirection pointer -----> static variables
(arena allocated)
For immutable memory: map_ptr & 1 == 1
map base pointer: slot 0
slot 1
+ map offset: slot 2 -----> static variables
slot 3 (gdb) print op_array->run_time_cache__ptr
$1 = (void ***) 0x791
(gdb) bt
#0 init_func_run_time_cache_i (op_array=0x408be830) at /path/to/php-src/Zend/zend_execute.c:3948
#1 0x0000555555d264c1 in init_func_run_time_cache (op_array=0x408be830) at /path/to/php-src/Zend/zend_execute.c:3956
#2 0x0000555555d39946 in ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER () at /path/to/php-src/Zend/zend_vm_execute.h:6846
#3 0x0000555555da1cca in execute_ex (ex=0x5555571353c0) at /path/to/php-src/Zend/zend_vm_execute.h:56351
#4 0x0000555555da66db in zend_execute (op_array=0x5555570d9270, return_value=0x0) at /path/to/php-src/Zend/zend_vm_execute.h:60123
#5 0x0000555555cef0c3 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /path/to/php-src/Zend/zend.c:1813
#6 0x0000555555c4bda1 in php_execute_script (primary_file=0x7fffffffc780) at /path/to/php-src/main/main.c:2539
#7 0x0000555555e63f34 in do_cli (argc=10, argv=0x555556e69e60) at /path/to/php-src/sapi/cli/php_cli.c:965
#8 0x0000555555e6503c in main (argc=10, argv=0x555556e69e60) at /path/to/php-src/sapi/cli/php_cli.c:1367
I forgot about the RC builds. Agreed, even if a fix was ready by then I don't expect that reviewers would be confident enough to approve it I wasn't sure of this from the original ticket - was that a segfault that happened some of the time or all of the time? |
I checked and found that the crash (with opcache.protect_memory=1) also occurs in php 7.4 (tested with 7.4.31-dev), which stopped receiving bug fix support 11 months ago - https://www.php.net/supported-versions.php (same stack trace of init_func_run_time_cache and ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER in gdb)
(the crash with protect_memory=1 is something I suspect is a symptom of a race condition that would possibly cause memory corruption in php 7.4 and 8.0 for late static binding) |
Looking at zend_persist_class_method , it just seems wrong. For the class in php 8.0, it has ZCG(is_immutable_class) == 0 (from late static binding?) I added debugging code, and it's initializing run_time_cache__ptr to a pointer within the shared memory arena, rather than to the offset corresponding to that pointer (ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem));) Changing to an offset might fix that, but I'm not clear on why the non-immutable class case was using arena_mem in the first place if (ZCG(is_immutable_class)) {
op_array->fn_flags |= ZEND_ACC_IMMUTABLE;
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
fprintf(stderr, "imm run_time_cache=%p\n", op_array->run_time_cache__ptr);
if (op_array->static_variables) {
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
}
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem));
fprintf(stderr, "arena_mem=%p run_time_cache=%p\n", ZCG(arena_mem), op_array->run_time_cache__ptr);
ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
} |
I have to wonder if something is causing the arena_mem to point into shared memory (zend_shared_alloc) instead of per-request(emalloced) memory in php 8.0. E.g. per-request would be the emalloced arena from zend_arena_alloc Possibly something to do with inheritance, since zend_accel_inheritance_cache_add changes ZCG(mem) to point into shared memory rather than per-request memory - the inheritance cache was changed in php 8.1, so I don't know if it no longer happens for all cases or just in the specific case
|
Anyway, my best guess as to why this crashes (in 7.4 and 8.0) is that the inheritance code causes the memory to be shared memory instead of per-request memory when this is compiled:
|
TysonAndre
When running with our standard production configuration with Opcache(Ofc, w/o the opcache.protect_memory=1) and PHP-FPM it starts on up-to n-th, there n is number of PHP-FPM child processes, and then crashes constantly on any following request, and it's probably matches your conclusion here:
This is indeed very interesting find. I didn't test it with our PHP 7.4 binaries(Which I stopped updating since we targeted 8.0 as upgrade target from our mainline 7.3), and was almost sure it was PHP 8.0 regression... May-be analyzing optimizer differences between 7.3 and 7.4 will put some light on it, and find the original change which broke it, and possibly fix it w/o changing allocation targets from SHM to heap, and similar large and undesirable changes to the engine. Thanks. cmb69
If only it was that easy... PHP version upgrades are a long complicated process in a business. We are migrating from 7.3 to 8.0 for the whole year now, with several types of servers running just fine with it. However during migration of one of servers which uses wider part of our codebase(Including inherited classes with late static binding) we started to receive lots of failures and saw tons of php-fpm process segfaults in the dmesg, resulting, ofc, in immediate revert to 'stable-and-proven' PHP 7.3 on this machine and halt of the migration project until further notice. It got through internal dev QA as most parts there pretested from CLI which has OPCache deactivated... Anyway, I quite understand the release process and that PHP 8.0 reaches its end of bug fix support quite soon. |
I am still having WordPress sites hang on segfault on PHP-fpm. This occurs after the site has been running for a while. I have run the sites on PHP 8.0, 8.1, 8.2 and 8.3. None stay up but 8.2 throws the error sooner than 8.3. |
Description
PHP-FPM crashes then OPCache enabled(Even if I disable all low 16bits of optimization flags in opcache.optimization_level) with some pattern of Late static binding involved.
Unfortunately I can't create minimal working test case(I tried my best).
The only thing I know is that pattern like this causes it in the end(Note the constant having initial value via 'self' and later used as LSB via 'static'):
Segmentation fault info:
PHP Version
PHP 8.0.22/8.0.23/8.024
8.1 tree seems to not be affected(Tested on 8.1.11).
Operating System
CentOS 7
The text was updated successfully, but these errors were encountered: