php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71482 Lost zend_string after "reached pm.max_children setting" with segfault
Submitted: 2016-01-29 09:12 UTC Modified: 2016-02-25 08:38 UTC
Votes:5
Avg. Score:5.0 ± 0.0
Reproduced:5 of 5 (100.0%)
Same Version:5 (100.0%)
Same OS:-5 (-100.0%)
From: alex dot schneider at sevenval dot com Assigned: laruence (profile)
Status: Closed Package: PCRE related
PHP Version: 7.0.2 OS: Linux Mint 17.3
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: alex dot schneider at sevenval dot com
New email:
PHP Version: OS:

 

 [2016-01-29 09:12 UTC] alex dot schneider at sevenval dot com
Description:
------------
The destruction of "HashTable module_registry" sometimes "throws" a segfault in module "pcre".

I can give you the following infos only:
* The segfault is "thrown" while the destruction of "HashTable module_registry" by "zend_hash_graceful_reverse_destroy(&module_registry)", but before "PHP_GSHUTDOWN_FUNCTION(pcre)"
* The trigger is the "ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)" function in zend_hash.c (line 1272) "if (EXPECTED(p->key)) {" with "Access to address 0xXXXXXXXXX is not allowed"

In the meantime while my test suite runs, I see in the log:

WARNING: [pool fit] server reached pm.max_children setting (2), consider raising it


I think, the bug is related to #63180.

My patch (attached here) works for me. Perhaps your experts can examine the differences here.

PS: Sorry for my english :)

Test script:
---------------
Unfortunately, i cannot isolate the trigger code from my very complex test suite with a lot of test methods and requests.

fpmlimits.conf for triggering the segfault:
...
pm = dynamic
pm.max_children = 2
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 2
pm.max_requests = 5
...

Expected result:
----------------
No segfaults

Actual result:
--------------
segfaults

Patches

php_pcre_segfault (last revision 2016-01-29 09:13 UTC by alex dot schneider at sevenval dot com)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-01-30 04:25 UTC] laruence@php.net
hmm, do you use any other extensions which is not bundled with php ?
 [2016-01-30 04:25 UTC] laruence@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: laruence
 [2016-01-30 05:34 UTC] alex dot schneider at sevenval dot com
-Status: Feedback +Status: Assigned
 [2016-01-30 05:34 UTC] alex dot schneider at sevenval dot com
Yes, we use, but i'a almost sure that the error comes from module "pcre" self.

Is also interesting that there is no longer segfaults, once we disable the "opcache".

Is here a big intern difference between the both functions (original and patched)?
 [2016-01-30 16:54 UTC] laruence@php.net
could you please show me the real backtrace? it may be helpful
 [2016-01-31 05:54 UTC] alex dot schneider at sevenval dot com
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000007a140d in gc_check_possible_root (z=0x7f523c5311e0) at ~/test/build/src/external/php-7.0.2/Zend/zend_gc.h:136
136			gc_possible_root(Z_COUNTED_P(z));
(gdb) bt
#0  0x00000000007a140d in gc_check_possible_root (z=0x7f523c5311e0) at ~/test/build/src/external/php-7.0.2/Zend/zend_gc.h:136
#1  0x00000000007a549d in zend_hash_clean (ht=0xdf9320) at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1355
#2  0x00000000004773a7 in make_subpats_table (num_subpats=0, pce=0x7fff9e3591c0) at ~/test/build/src/external/php-7.0.2/ext/pcre/php_pcre.c:226
#3  0x000000000079ad42 in do_register_internal_class (orig_class_entry=0x79ad42 <do_register_internal_class+6>, ce_flags=32767)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_API.c:2641
#4  0x000000000078ef00 in zend_startup (utility_functions=0x27df3e0, extensions=0x7fff9e359380) at ~/test/build/src/external/php-7.0.2/Zend/zend.c:708
#5  0x00000000007a4a8a in zend_hash_del_ind (ht=0x27df3e0, key=0x7fff9e359380) at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1106
#6  0x00000000007a4b55 in zend_hash_str_del_ind (ht=0x7a4b55 <zend_hash_str_del_ind+12>, str=0x7fff9e3592d0 "", len=4294967313)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1128
#7  0x00000000007a5e16 in zend_hash_apply_with_arguments (ht=0x78ef00 <zend_startup+670>, apply_func=0x7fff9e359230, num_args=0)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1571
#8  0x0000000000798a7e in zend_check_magic_method_implementation (ce=0x7a5e16 <zend_hash_apply_with_arguments+192>, fptr=0x7fff9e359300, error_type=0)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_API.c:2062
#9  0x000000000078f582 in zend_call_destructors () at ~/test/build/src/external/php-7.0.2/Zend/zend.c:953
#10 0x00000000006fe1ba in php_execute_script (primary_file=0x0) at ~/test/build/src/external/php-7.0.2/main/main.c:2473
#11 0x00000000008610e1 in ?? ()
#12 0x0000000000000000 in ?? ()
(gdb) bt full
#0  0x00000000007a140d in gc_check_possible_root (z=0x7f523c5311e0) at ~/test/build/src/external/php-7.0.2/Zend/zend_gc.h:136
No locals.
#1  0x00000000007a549d in zend_hash_clean (ht=0xdf9320) at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1355
        p = 0x29b8180
        end = 0x29b8240
#2  0x00000000004773a7 in make_subpats_table (num_subpats=0, pce=0x7fff9e3591c0) at ~/test/build/src/external/php-7.0.2/ext/pcre/php_pcre.c:226
        extra = 0xc
        name_cnt = 43745856
        name_size = 43745664
        ni = 0
        rc = 32767
        name_table = 0x4773a7 <make_subpats_table+76> "E\334H\213E\260H\213"
        name_idx = 0
        subpat_names = 0xdf9320
        rc1 = 0
        rc2 = -1640656416
#3  0x000000000079ad42 in do_register_internal_class (orig_class_entry=0x79ad42 <do_register_internal_class+6>, ce_flags=32767)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_API.c:2641
        class_entry = 0x7fff9e359230
        lowercase_name = 0x27df3e0
#4  0x000000000078ef00 in zend_startup (utility_functions=0x27df3e0, extensions=0x7fff9e359380) at ~/test/build/src/external/php-7.0.2/Zend/zend.c:708
No locals.
#5  0x00000000007a4a8a in zend_hash_del_ind (ht=0x27df3e0, key=0x7fff9e359380) at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1106
---Type <return> to continue, or q <return> to quit---
        data = 0x119e3594f0
        h = 15539213552
        nIndex = 0
        idx = 0
        p = 0xdfe080
        prev = 0x27c3000
#6  0x00000000007a4b55 in zend_hash_str_del_ind (ht=0x7a4b55 <zend_hash_str_del_ind+12>, str=0x7fff9e3592d0 "", len=4294967313)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1128
        h = 14672000
        nIndex = 0
        idx = 41693184
        p = 0x3ffffffef
        prev = 0x300abc580
#7  0x00000000007a5e16 in zend_hash_apply_with_arguments (ht=0x78ef00 <zend_startup+670>, apply_func=0x7fff9e359230, num_args=0)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_hash.c:1571
        idx = 0
        p = 0x7fff9e359280
        args = {{gp_offset = 8014474, fp_offset = 0, overflow_arg_area = 0x0, reg_save_area = 0x27c3000}}
        hash_key = {h = 140735847699328, key = 0x27df3e0}
        result = 0
#8  0x0000000000798a7e in zend_check_magic_method_implementation (ce=0x7a5e16 <zend_hash_apply_with_arguments+192>, fptr=0x7fff9e359300, error_type=0)
    at ~/test/build/src/external/php-7.0.2/Zend/zend_API.c:2062
        lcname = "p\227\065\236\003\000\000\000\000\060|\002\000\000\000"
---Type <return> to continue, or q <return> to quit---
        name_len = 14672000
#9  0x000000000078f582 in zend_call_destructors () at ~/test/build/src/external/php-7.0.2/Zend/zend.c:953
        __orig_bailout = 0x100000011
        __bailout = {{__jmpbuf = {140735847699152, 8014677, 140735847700336, 41693184, 12896159104, 14672000, 17179869167, 0}, __mask_was_saved = -1640656128, __saved_mask = {__val = {
                8019478, 140735847699216, 14672000, 15539214192, 41693184, 140735847699216, 7965310, 140735847699296, 7927170, 140735847699312, 4356944, 140735847700336, 0, 0, 0, 
                140735847699296}}}}
#10 0x00000000006fe1ba in php_execute_script (primary_file=0x0) at ~/test/build/src/external/php-7.0.2/main/main.c:2473
        realfile = "@ \222VR\177\000\000\000 \222VR\177\000\000Ѓ5\236\377\177\000\000\356\252u\000\000\000\000\000\020l\215VR\177", '\000' <repeats 14 times>, "D\005\000\000\200ū\000\000\000\000\000\000 \222VR\177\000\000@\000\200VR\177\000\000\020\204\065\236\"\001\000\000\t\000\000\200\000\000\000\000\000 \022\000\000\000\000\000\000\000\200VR\177\000\000\000\204\065\236\377\177\000\000,\324u", '\000' <repeats 17 times>, "D\005\000\000\200ū\000\000\000\000\000\000 \222VR\177\000\000@\204\065\236\377\177\000\000\033Xz\000\000\000\000\000@\204\065\236\377\177\000\000\000 \222VR\177\000\000\000\000\000\000\000\000\000\000(\342\220"...
        __orig_bailout = 0x0
        __bailout = {{__jmpbuf = {140735847695040, 34367460396, 139991616154112, 139991615275072, 139991616154160, 139991616154112, 140735847695136, 7711470}, 
            __mask_was_saved = 1452420832, __saved_mask = {__val = {0, 1129576398848, 11249520, 139991616154112, 139991615275072, 919123001344, 2147483656, 879104, 139991615275008, 
                140735847695184, 7722028, 0, 1129576398848, 11249520, 40106813952, 139991616462848}}}}
        prepend_file_p = 0x0
        append_file_p = 0x0
        prepend_file = {handle = {fd = 0, fp = 0x0, stream = {handle = 0x0, isatty = 0, mmap = {len = 0, pos = 0, map = 0x0, buf = 0x0, old_handle = 0x0, old_closer = 0x0}, reader = 0x0, 
              fsizer = 0x0, closer = 0x31ba}}, filename = 0x800000001 <error: Cannot access memory at address 0x800000001>, opened_path = 0x7f52568d6910, type = 1451229248, 
          free_filename = 82 'R'}
        append_file = {handle = {fd = 1452108096, fp = 0x7f52568d6940, stream = {handle = 0x7f52568d6940, isatty = 1452108048, mmap = {len = 140735847694992, pos = 7711470, map = 0x13880, 
                buf = 0x0, old_handle = 0x10700000000, old_closer = 0xaba770}, reader = 0x7f52568d6910, fsizer = 0x7f5256800040, closer = 0xd600000000}}, 
          filename = 0x80000008 <error: Cannot access memory at address 0x80000008>, opened_path = 0xd6910, type = 1451229184, free_filename = 82 'R'}
---Type <return> to continue, or q <return> to quit---
        old_cwd = 0x0
        use_heap = 0 '\000'
        retval = 0
#11 0x00000000008610e1 in ?? ()
No symbol table info available.
#12 0x0000000000000000 in ?? ()
No symbol table info available.
 [2016-02-01 08:36 UTC] laruence@php.net
in generally, your patch is right. we need a persistent alloc memory for the key

but I am prefer to find out which extension(case) break this.. 

the only reason I can think out is, that some interned string in a extension used as a pcre pattern.. 

I just don't know which extension and how does it do this..
 [2016-02-01 09:26 UTC] alex dot schneider at sevenval dot com
I agree. I could not find out which module was responsible, too.

I made debug outputs while destructoring of the "module_registry" and saw that it crashes right here, in the "pcre" module. But i don't know, who is the trigger.
 [2016-02-08 03:18 UTC] laruence@php.net
could you please try with valgrind?

or could you grant me a ssh access to a reproducible box(mail)?

thanks
 [2016-02-08 20:40 UTC] alex dot schneider at sevenval dot com
Sorry, but it's not possible to run my complex test suite with/via valgrind.

I have run tests in a subdirectory in my project and the error/segfault has occurred each time in an other test, but always with the same gdb-backtrace (see above).

My guess is, that any caller of "pcre_get_compiled_regex_cache()" sometimes wrong flags the "zend string *regex" and releases them afterwards. I would not trust to any caller here and ALWAYS duplicate and make persistent the string (without any checks).
 [2016-02-25 08:38 UTC] laruence@php.net
-Status: Assigned +Status: Closed
 [2016-02-25 08:38 UTC] laruence@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.

this should be fixed in svn
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Tue Oct 22 16:01:28 2019 UTC