Skip to content

Browscap crashes PHP 8.1.12 on request shutdown (apache2) #10052

Closed

Description

@nradchenko

Description

The following code:

<?php
$default_browser='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Safari/537.36';
$browser = get_browser($default_browser, true);
print_r($browser);

Resulted in "zend_mm_heap corrupted" and exit code 1.

But I expected this output instead (or similar):

Array
(
    [browser_name_regex] => ~^mozilla/5\.0 \(.*linux.*x86_64.*\) applewebkit.* \(.*khtml.*like.*gecko.*\) chrome/104\.0.*safari/.*$~
    [browser_name_pattern] => Mozilla/5.0 (*Linux*x86_64*) applewebkit* (*khtml*like*gecko*) Chrome/104.0*Safari/*
    [parent] => Chrome 104.0
    [browser_bits] => 64
    [platform] => Linux
    [platform_description] => Linux
    [platform_bits] => 64
    [platform_maker] => Linux Foundation
    [device_name] => Linux Desktop
    [device_code_name] => Linux Desktop
    [comment] => Chrome 104.0
    [browser] => Chrome
    [browser_type] => Browser
    [browser_maker] => Google Inc
    [version] => 104.0
    [majorver] => 104
    [frames] => 1
    [iframes] => 1
    [tables] => 1
    [cookies] => 1
    [javascript] => 1
    [cssversion] => 3
    [aolversion] => 0
    [device_type] => Desktop
    [device_pointing_method] => mouse
    [renderingengine_name] => Blink
    [renderingengine_description] => a WebKit Fork by Google
    [renderingengine_maker] => Google Inc
    [browser_modus] => unknown
    [minorver] => 0
    [platform_version] => unknown
    [alpha] =>
    [beta] =>
    [win16] =>
    [win32] =>
    [win64] =>
    [backgroundsounds] =>
    [vbscript] =>
    [javaapplets] =>
    [activexcontrols] =>
    [ismobiledevice] =>
    [istablet] =>
    [issyndicationreader] =>
    [crawler] =>
    [isfake] =>
    [isanonymized] =>
    [ismodified] =>
    [device_maker] => unknown
    [device_brand_name] => unknown
    [renderingengine_version] => unknown
)
(gdb) bt
#0  0x00007fc84f335140 in write () from target:/lib/x86_64-linux-gnu/libc.so.6
#1  0x00007fc84f2b01bd in _IO_file_write () from target:/lib/x86_64-linux-gnu/libc.so.6
#2  0x00007fc84f2b0b2f in _IO_file_xsputn () from target:/lib/x86_64-linux-gnu/libc.so.6
#3  0x00007fc84f283707 in ?? () from target:/lib/x86_64-linux-gnu/libc.so.6
#4  0x00007fc84f280726 in vfprintf () from target:/lib/x86_64-linux-gnu/libc.so.6
#5  0x00007fc84f3574c6 in __fprintf_chk () from target:/lib/x86_64-linux-gnu/libc.so.6
#6  0x00007fc84d9e4d4b in fprintf (__fmt=0x7fc84ddeee94 "%s\n", __stream=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/stdio2.h:97
#7  zend_mm_panic (message=0x7fc84e627183 "zend_mm_heap corrupted") at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_alloc.c:359
#8  0x00007fc84dcd8301 in zend_mm_free_heap (ptr=<optimized out>, heap=<optimized out>) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_alloc.c:1369
#9  _efree (ptr=<optimized out>) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_alloc.c:2552
#10 0x00007fc84dbfff7b in zend_string_release_ex (persistent=false, s=<optimized out>) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_string.h:336
#11 browscap_entry_dtor (zvalue=<optimized out>) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/ext/standard/browscap.c:73
#12 0x00007fc84dd124d2 in zend_hash_destroy (ht=0x7fc842a03380) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_hash.c:1602
#13 0x00007fc84dbffe31 in browscap_bdata_dtor (bdata=bdata@entry=0x7fc84e9a69a0 <browscap_globals>, persistent=persistent@entry=0)
    at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/ext/standard/browscap.c:464
#14 0x00007fc84dc01502 in zm_deactivate_browscap (type=type@entry=1, module_number=module_number@entry=38) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/ext/standard/browscap.c:523
#15 0x00007fc84dbf8f65 in zm_deactivate_basic (type=1, module_number=38) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/ext/standard/basic_functions.c:562
#16 0x00007fc84dd0751b in zend_deactivate_modules () at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/Zend/zend_API.c:3046
#17 0x00007fc84dc9b8a5 in php_request_shutdown (dummy=dummy@entry=0x0) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/main/main.c:1824
#18 0x00007fc84dde89e7 in php_apache_request_dtor (r=0x563a7943d840) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/sapi/apache2handler/sapi_apache2.c:546
#19 php_handler (r=<optimized out>) at /build/tmp-build/php-src-tag-beget-version-8.1.12-1/sapi/apache2handler/sapi_apache2.c:718
#20 0x0000563a5626dc30 in ap_run_handler (r=r@entry=0x563a7943d840) at config.c:169
#21 0x0000563a5626e1ad in ap_invoke_handler (r=r@entry=0x563a7943d840) at config.c:443
#22 0x0000563a562a19bb in ap_process_async_request (r=0x563a7943d840) at http_request.c:452
#23 0x0000563a562a1b9e in ap_process_request (r=r@entry=0x563a7943d840) at http_request.c:487
#24 0x0000563a5629ddf4 in ap_process_http_sync_connection (c=0x563a79366800) at http_core.c:208
#25 ap_process_http_connection (c=0x563a79366800) at http_core.c:249
#26 0x0000563a56277770 in ap_run_process_connection (c=c@entry=0x563a79366800) at connection.c:42
#27 0x00007fc84e9b4087 in itk_fork_process (c=0x563a79366800) at mpm_itk.c:213
#28 0x0000563a56277770 in ap_run_process_connection (c=c@entry=0x563a79366800) at connection.c:42
#29 0x0000563a56277c9e in ap_process_connection (c=c@entry=0x563a79366800, csd=<optimized out>) at connection.c:210
#30 0x0000563a563018c8 in child_main (child_num_arg=child_num_arg@entry=8, child_bucket=child_bucket@entry=0) at prefork.c:654
#31 0x0000563a56301bd0 in make_child (s=0x563a56bfc418, slot=8) at prefork.c:756
#32 0x0000563a5630262a in perform_idle_server_maintenance (p=<optimized out>) at prefork.c:860
#33 prefork_run (_pconf=<optimized out>, plog=<optimized out>, s=<optimized out>) at prefork.c:1053
#34 0x0000563a5624f08e in ap_run_mpm (pconf=0x563a56bc5388, plog=0x563a56c032f8, s=0x563a56bfc418) at mpm_common.c:95
#35 0x0000563a56246ce6 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at main.c:894
(gdb) 

PHP Version

8.1.12

Operating System

Ubuntu 18.04

Activity

  1. cmb69 commented on Dec 5, 2022

    @cmb69
    Member

    What is the value of the php.ini setting browscap? Is it a relative path?

  2. nradchenko commented on Dec 5, 2022

    @nradchenko
    Author

    Nope, it's an absolute one - /home/x/xxxxxxxx/xxxx/public_html/browscap.ini (redacted, the length is the same as of original).

    The file itself is lite_php_browscap.ini.

  3. cmb69 commented on Dec 6, 2022

    @cmb69
    Member

    Thanks for the feedback. I still can't reproduce this issue, though. Looking at the code reveals no issues either, although there is a comment which hints at potential issues:

    /* OBJECTS_FIXME: This whole extension needs going through. The use of objects looks pretty broken here */

    Since this is about apache2handler, the issue might only happen with certain MPMs. I'm using mpm_winnt and there is "just" a memory leak, because PHP is started up in the control process, which is never used to handle requests (see #7865).

    Which MPM do you use?

  4. nradchenko commented on Dec 6, 2022

    @nradchenko
    Author

    It's apache 2.4.51 with mpm-itk.

    Also this is the minimal reproducer I could come up with:

    <?php
    get_browser();
    [foo]
    Comment="foo"
    
    [bar]
    Parent="foo"
    php_admin_value browscap "/home/x/xxxxxxxx/xxxxxxxxxxxxxxxxxxx/public_html/browscap.ini"
    
  5. 2 remaining items

  6. nradchenko commented on Dec 7, 2022

    @nradchenko
    Author

    My guess was correct, bisect has revealed that 5eb1f92 is the first bad commit. I'd be happy if someone explains me what is the refactoring behind these changes, and how could they particularly affect mpm-itk.

    Although the commit affects a bunch of other extension files, the only issue of a kind I know about so far is the browscap problem. Should I run some tests to make sure that my SAPI is sane? I know that PHP has a lot of tests already written, but they are usually run within CLI, and I don't know how to run them for apache2. Is that possible?

    Thanks.

  7. self-assigned this
    on Dec 7, 2022
  8. cmb69 commented on Dec 7, 2022

    @cmb69
    Member

    The Zend Engine distinguishes between persistent and regular memory allocations. Regular allocation are supposed to be freed after each request; persistent allocations are supposed to be freed when the process shuts down.

    zend_string_release_ex() expects the user to provide the proper persistent flag, while zend_string_release() figures that out by itself, but is a bit slower. It looks like some of these changes are not correct (apparently, a persistent string is released as regular string). It is possible, that this only affects mpm-itk, which appears to use some uncommon approach regarding request handling (fork on each request).

  9. cmb69 commented on Dec 13, 2022

    @cmb69
    Member

    php_admin_value browscap "/home/x/xxxxxxxx/xxxxxxxxxxxxxxxxxxx/public_html/browscap.ini"

    Ah, I think that explains the problem (previously, I tried the setting in php.ini, and that didn't cause issues). If I revert the respective changes to browscap.c, I can now go beyond the original failure, but still get an assertion violation in zend_shutdown_executor_values().

    Anyhow, it seems that string interning is the concrete issue, but a more general problem is that the browscap ini is read again for every request what is wasteful. I shall have a closer look.

  10. added a commit that references this issue on Mar 19, 2023
    a0d7b6a
  11. added 2 commits that reference this issue on Mar 20, 2023
    c407243
    64bb3ef
  12. added a commit that references this issue on Mar 20, 2023
    0fa9afb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions