php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77338 get_browser with empty string
Submitted: 2018-12-22 23:02 UTC Modified: 2018-12-24 11:22 UTC
From: emondpph at gmail dot com Assigned: nikic (profile)
Status: Closed Package: Reproducible crash
PHP Version: 7.3.0 OS: Mac OS Mojave
Private report: No CVE-ID: None
 [2018-12-22 23:02 UTC] emondpph at gmail dot com
Description:
------------
I noticed this bug while doing a batch of assertions on PHP 7.3.

get_browser('') will crash PHP.

In PHP 7.2 it would return an array.

Test script:
---------------
<?php
var_dump(get_browser(''));
?>

Expected result:
----------------
object(stdClass)#1 (10) { ["browser_name_regex"]=> string(6) "~^.*$~" ["browser_name_pattern"]=> string(1) "*" ["parent"]=> string(17) "DefaultProperties" ["comment"]=> string(15) "Default Browser" ["browser"]=> string(15) "Default Browser" ["ismobiledevice"]=> string(0) "" ["istablet"]=> string(0) "" ["version"]=> string(3) "0.0" ["platform"]=> string(7) "unknown" ["device_type"]=> string(7) "unknown" }

Actual result:
--------------
Crash

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-12-22 23:03 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2018-12-22 23:03 UTC] requinix@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a backtrace to see what is happening behind the scenes. To
find out how to generate a backtrace, please read
http://bugs.php.net/bugs-generating-backtrace.php for *NIX and
http://bugs.php.net/bugs-generating-backtrace-win32.php for Win32

Once you have generated a backtrace, please submit it to this bug
report and change the status back to "Open". Thank you for helping
us make PHP better.


 [2018-12-22 23:19 UTC] emondpph at gmail dot com
Here is what I get in the apache log when I run the script. For the backtrace, I don't know how to get this done from my setup, sorry.
zend_mm_heap corrupted
 [2018-12-23 12:30 UTC] cmb@php.net
-Status: Feedback +Status: Verified
 [2018-12-23 12:30 UTC] cmb@php.net
I can confirm this with browscap.ini version 6000031.  I get the
following backtrace:

    #0  0x0000000008363745 in zend_mm_free_heap (heap=0x7ffffde00040,
        ptr=0x8af8250,
        __zend_filename=0x8556670 "/mnt/c/Users/cmb/php-dev/php-src/ext/standard/browscap.c", __zend_lineno=758, __zend_orig_filename=0x0, __zend_orig_lineno=0)
        at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend_alloc.c:1398
    #1  0x000000000836621c in _efree (ptr=0x8af8250,
        __zend_filename=0x8556670 "/mnt/c/Users/cmb/php-dev/php-src/ext/standard/browscap.c", __zend_lineno=758, __zend_orig_filename=0x0, __zend_orig_lineno=0)
        at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend_alloc.c:2513
    #2  0x00000000082592fb in zif_get_browser (execute_data=0x7ffffde1e100,
        return_value=0x7ffffde1e080)
        at /mnt/c/Users/cmb/php-dev/php-src/ext/standard/browscap.c:758
    #3  0x00000000083fbffd in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER ()
        at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend_vm_execute.h:690
    #4  0x000000000846312b in execute_ex (ex=0x7ffffde1e030)
        at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend_vm_execute.h:55418
    #5  0x0000000008468739 in zend_execute (op_array=0x7ffffde7b300,
        return_value=0x0)
        at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend_vm_execute.h:60834
    #6  0x000000000839a698 in zend_execute_scripts (type=8, retval=0x0,
        file_count=3) at /mnt/c/Users/cmb/php-dev/php-src/Zend/zend.c:1568
    #7  0x0000000008310dfb in php_execute_script (primary_file=0x7ffffffedee0)
        at /mnt/c/Users/cmb/php-dev/php-src/main/main.c:2630
    #8  0x000000000846b12d in do_cli (argc=3, argv=0x8aedd00)

valgrind reports:

    ==10214== Memcheck, a memory error detector
    ==10214== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
    ==10214== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
    ==10214== Command: sapi/cli/php -dbrowscap=/mnt/c/Users/cmb/php-dev/browscap.ini ../77338.php
    ==10214==
    bool(false)
    ==10214== Invalid read of size 4
    ==10214==    at 0x35E9AD: zend_string_release (zend_string.h:275)
    ==10214==    by 0x36047E: browscap_bdata_dtor (browscap.c:488)
    ==10214==    by 0x36063A: zm_shutdown_browscap (browscap.c:551)
    ==10214==    by 0x3526C3: zm_shutdown_basic (basic_functions.c:3775)
    ==10214==    by 0x4AC115: module_destructor (zend_API.c:2575)
    ==10214==    by 0x4A0BEB: module_destructor_zval (zend.c:745)
    ==10214==    by 0x4B67B1: _zend_hash_del_el_ex (zend_hash.c:1181)
    ==10214==    by 0x4B687D: _zend_hash_del_el (zend_hash.c:1204)
    ==10214==    by 0x4B80D4: zend_hash_graceful_reverse_destroy (zend_hash.c:1658)
    ==10214==    by 0x4A9EE7: zend_destroy_modules (zend_API.c:2013)
    ==10214==    by 0x4A1048: zend_shutdown (zend.c:976)
    ==10214==    by 0x418A2A: php_module_shutdown (main.c:2489)
    ==10214==  Address 0x6176e54 is 4 bytes inside a block of size 32 free'd
    ==10214==    at 0x4C2CDDB: free (vg_replace_malloc.c:530)
    ==10214==    by 0x46E1F1: _efree (zend_alloc.c:2508)
    ==10214==    by 0x3612FA: zif_get_browser (browscap.c:758)
    ==10214==    by 0x503FFC: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:690)
    ==10214==    by 0x56B12A: execute_ex (zend_vm_execute.h:55418)
    ==10214==    by 0x570738: zend_execute (zend_vm_execute.h:60834)
    ==10214==    by 0x4A2697: zend_execute_scripts (zend.c:1568)
    ==10214==    by 0x418DFA: php_execute_script (main.c:2630)
    ==10214==    by 0x57312C: do_cli (php_cli.c:997)
    ==10214==    by 0x573FE2: main (php_cli.c:1389)
    ==10214==  Block was alloc'd at
    ==10214==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
    ==10214==    by 0x46EE6D: __zend_malloc (zend_alloc.c:2904)
    ==10214==    by 0x4DA185: zend_string_alloc (zend_string.h:133)
    ==10214==    by 0x4DA9FF: zend_interned_strings_init (zend_string.c:98)
    ==10214==    by 0x4A0F76: zend_startup (zend.c:889)
    ==10214==    by 0x417F1A: php_module_startup (main.c:2179)
    ==10214==    by 0x5721A7: php_cli_startup (php_cli.c:420)
    ==10214==    by 0x573F5A: main (php_cli.c:1356)
    ==10214==
    ==10214== Invalid read of size 4
    ==10214==    at 0x4C6C3A: zend_string_release (zend_string.h:275)
    ==10214==    by 0x4C70AF: free_ini_entry (zend_ini.c:91)
    ==10214==    by 0x4B67B1: _zend_hash_del_el_ex (zend_hash.c:1181)
    ==10214==    by 0x4B687D: _zend_hash_del_el (zend_hash.c:1204)
    ==10214==    by 0x4B8351: zend_hash_apply_with_argument (zend_hash.c:1716)
    ==10214==    by 0x4C75B4: zend_unregister_ini_entries (zend_ini.c:278)
    ==10214==    by 0x3E71F7: zm_shutdown_url_scanner_ex (url_scanner_ex.re:979)
    ==10214==    by 0x352744: zm_shutdown_basic (basic_functions.c:3778)
    ==10214==    by 0x4AC115: module_destructor (zend_API.c:2575)
    ==10214==    by 0x4A0BEB: module_destructor_zval (zend.c:745)
    ==10214==    by 0x4B67B1: _zend_hash_del_el_ex (zend_hash.c:1181)
    ==10214==    by 0x4B687D: _zend_hash_del_el (zend_hash.c:1204)
    ==10214==  Address 0x6176e54 is 4 bytes inside a block of size 32 free'd
    ==10214==    at 0x4C2CDDB: free (vg_replace_malloc.c:530)
    ==10214==    by 0x46E1F1: _efree (zend_alloc.c:2508)
    ==10214==    by 0x3612FA: zif_get_browser (browscap.c:758)
    ==10214==    by 0x503FFC: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:690)
    ==10214==    by 0x56B12A: execute_ex (zend_vm_execute.h:55418)
    ==10214==    by 0x570738: zend_execute (zend_vm_execute.h:60834)
    ==10214==    by 0x4A2697: zend_execute_scripts (zend.c:1568)
    ==10214==    by 0x418DFA: php_execute_script (main.c:2630)
    ==10214==    by 0x57312C: do_cli (php_cli.c:997)
    ==10214==    by 0x573FE2: main (php_cli.c:1389)
    ==10214==  Block was alloc'd at
    ==10214==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
    ==10214==    by 0x46EE6D: __zend_malloc (zend_alloc.c:2904)
    ==10214==    by 0x4DA185: zend_string_alloc (zend_string.h:133)
    ==10214==    by 0x4DA9FF: zend_interned_strings_init (zend_string.c:98)
    ==10214==    by 0x4A0F76: zend_startup (zend.c:889)
    ==10214==    by 0x417F1A: php_module_startup (main.c:2179)
    ==10214==    by 0x5721A7: php_cli_startup (php_cli.c:420)
    ==10214==    by 0x573F5A: main (php_cli.c:1356)
    ==10214==
    ==10214== Invalid read of size 4
    ==10214==    at 0x4DA8EE: _str_dtor (zend_string.c:62)
    ==10214==    by 0x4B7228: zend_hash_destroy (zend_hash.c:1417)
    ==10214==    by 0x4DAB28: zend_interned_strings_dtor (zend_string.c:118)
    ==10214==    by 0x418A68: php_module_shutdown (main.c:2514)
    ==10214==    by 0x57403D: main (php_cli.c:1404)
    ==10214==  Address 0x6176e54 is 4 bytes inside a block of size 32 free'd
    ==10214==    at 0x4C2CDDB: free (vg_replace_malloc.c:530)
    ==10214==    by 0x46E1F1: _efree (zend_alloc.c:2508)
    ==10214==    by 0x3612FA: zif_get_browser (browscap.c:758)
    ==10214==    by 0x503FFC: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:690)
    ==10214==    by 0x56B12A: execute_ex (zend_vm_execute.h:55418)
    ==10214==    by 0x570738: zend_execute (zend_vm_execute.h:60834)
    ==10214==    by 0x4A2697: zend_execute_scripts (zend.c:1568)
    ==10214==    by 0x418DFA: php_execute_script (main.c:2630)
    ==10214==    by 0x57312C: do_cli (php_cli.c:997)
    ==10214==    by 0x573FE2: main (php_cli.c:1389)
    ==10214==  Block was alloc'd at
    ==10214==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
    ==10214==    by 0x46EE6D: __zend_malloc (zend_alloc.c:2904)
    ==10214==    by 0x4DA185: zend_string_alloc (zend_string.h:133)
    ==10214==    by 0x4DA9FF: zend_interned_strings_init (zend_string.c:98)
    ==10214==    by 0x4A0F76: zend_startup (zend.c:889)
    ==10214==    by 0x417F1A: php_module_startup (main.c:2179)
    ==10214==    by 0x5721A7: php_cli_startup (php_cli.c:420)
    ==10214==    by 0x573F5A: main (php_cli.c:1356)
    ==10214==
    ==10214== Invalid free() / delete / delete[] / realloc()
    ==10214==    at 0x4C2CDDB: free (vg_replace_malloc.c:530)
    ==10214==    by 0x4DA90C: _str_dtor (zend_string.c:62)
    ==10214==    by 0x4B7228: zend_hash_destroy (zend_hash.c:1417)
    ==10214==    by 0x4DAB28: zend_interned_strings_dtor (zend_string.c:118)
    ==10214==    by 0x418A68: php_module_shutdown (main.c:2514)
    ==10214==    by 0x57403D: main (php_cli.c:1404)
    ==10214==  Address 0x6176e50 is 0 bytes inside a block of size 32 free'd
    ==10214==    at 0x4C2CDDB: free (vg_replace_malloc.c:530)
    ==10214==    by 0x46E1F1: _efree (zend_alloc.c:2508)
    ==10214==    by 0x3612FA: zif_get_browser (browscap.c:758)
    ==10214==    by 0x503FFC: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:690)
    ==10214==    by 0x56B12A: execute_ex (zend_vm_execute.h:55418)
    ==10214==    by 0x570738: zend_execute (zend_vm_execute.h:60834)
    ==10214==    by 0x4A2697: zend_execute_scripts (zend.c:1568)
    ==10214==    by 0x418DFA: php_execute_script (main.c:2630)
    ==10214==    by 0x57312C: do_cli (php_cli.c:997)
    ==10214==    by 0x573FE2: main (php_cli.c:1389)
    ==10214==  Block was alloc'd at
    ==10214==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
    ==10214==    by 0x46EE6D: __zend_malloc (zend_alloc.c:2904)
    ==10214==    by 0x4DA185: zend_string_alloc (zend_string.h:133)
    ==10214==    by 0x4DA9FF: zend_interned_strings_init (zend_string.c:98)
    ==10214==    by 0x4A0F76: zend_startup (zend.c:889)
    ==10214==    by 0x417F1A: php_module_startup (main.c:2179)
    ==10214==    by 0x5721A7: php_cli_startup (php_cli.c:420)
    ==10214==    by 0x573F5A: main (php_cli.c:1356)
    ==10214==
    ==10214==
    ==10214== HEAP SUMMARY:
    ==10214==     in use at exit: 446,368 bytes in 10,237 blocks
    ==10214==   total heap usage: 941,165 allocs, 930,929 frees, 84,327,874 bytes allocated
    ==10214==
    ==10214== LEAK SUMMARY:
    ==10214==    definitely lost: 446,312 bytes in 10,236 blocks
    ==10214==    indirectly lost: 0 bytes in 0 blocks
    ==10214==      possibly lost: 56 bytes in 1 blocks
    ==10214==    still reachable: 0 bytes in 0 blocks
    ==10214==         suppressed: 0 bytes in 0 blocks
    ==10214== Rerun with --leak-check=full to see details of leaked memory
    ==10214==
    ==10214== For counts of detected and suppressed errors, rerun with: -v
    ==10214== ERROR SUMMARY: 958 errors from 4 contexts (suppressed: 0 from 0)
 [2018-12-23 18:25 UTC] nikic@php.net
-Assigned To: +Assigned To: nikic
 [2018-12-23 18:30 UTC] nikic@php.net
There's two things wrong here: An incorrect efree() that should be a zend_string_release() (also exists in 7.2), and the fact that we hit this code path at all on 7.3, which indicates a presumably not intended change in behavior.
 [2018-12-23 18:49 UTC] cmb@php.net
The latter is due to a confusion regarding re_options[1]; these
are reported as 8, because PREG_JIT has been set[2], but the
following pcre2_match() interprets them as PCRE2_NOTEMPTY_ATSTART,
yielding no match.  This might be a general problem affecting
PCRE.

[1] <https://github.com/php/php-src/blob/php-7.3.0/ext/standard/browscap.c#L623>
[2] <https://github.com/php/php-src/blob/php-7.3.0/ext/pcre/php_pcre.c#L783>
 [2018-12-23 19:23 UTC] nikic@php.net
I've fixed the invalid efree in 7.2+ via https://github.com/php/php-src/commit/64de5bc224584e1da08c5c2bdf76db72ccbaaaab. The test case is a variation that makes this crash there as well, by not having any default.

We still need to fix the PCRE issue.
 [2018-12-23 19:39 UTC] nikic@php.net
@cmb: Am I reading this right that the re_options parameter is basically bogus, because we set all the relevant options during compilation and the parts that are part of preg_options are not relevant for manual PCRE calls?

Ideally we'd just drop this parameter to avoid confusion, but as we can't do that for 7.3 we should just always set it to zero.
 [2018-12-24 11:22 UTC] cmb@php.net
> Am I reading this right that the re_options parameter is basically
> bogus, because we set all the relevant options during compilation
> and the parts that are part of preg_options are not relevant for
> manual PCRE calls?

I'm not sure.  It might be relevant to get the information whether
the regexp has been compiled with or without JIT (i.e. whether
PREG_JIT is set).

Anyhow, the options that have been retrieved via the re_options
parameter of pcre_get_compiled_regex() must never be passed to the
options parameter of pcre2_match(), since these are different
bitsets.
 [2018-12-26 16:13 UTC] nikic@php.net
Automatic comment on behalf of nikita.ppv@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b1deb98c42328488cfce61d49d9607ed44fff7a4
Log: Fixed bug #77338
 [2018-12-26 16:13 UTC] nikic@php.net
-Status: Verified +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 09:01:32 2024 UTC