php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80873 FFI crashes with segmentation fault when calling cob_init()
Submitted: 2021-03-16 16:53 UTC Modified: 2021-03-21 19:57 UTC
From: Elmar dot Klausmeier at gmail dot com Assigned:
Status: Closed Package: ffi (PECL)
PHP Version: 7.4.16 OS: Linux
Private report: No CVE-ID: None
 [2021-03-16 16:53 UTC] Elmar dot Klausmeier at gmail dot com
Description:
------------
I tried this with PHP versions 8.0.3 and 7.4.16. I tried it on Arch Linux 5.11.6 and 4.14.180. So version of PHP and operating system does not seem to matter. I tried php, php-cgi in both versions 7 and 8.

I changed php.ini to contain:
extension=ffi
ffi.enable=true

I checked that FFI by itself works. I used printf() from libc.so.6 (the example from the documentation) and j0() from libm.so.6 as examples, and they both produce the desired result -- no crash.

Task at hand: Call Cobol (=GnuCobol) from PHP. For this, you have to first call cob_init() or cob_init_nomain(), which initializes GnuCobol. I tried both initialization routines -- always same "segmentation fault". After cob_init() you call your Cobol program, but in our case this does not matter, because segmentation fault comes regardless of called Cobol program. I also tried to call cob_tidy(), the endgame of Cobol, but this also did not change the outcome. It looks that FFI and cob_init() do not like each other. Just for clarity: I can call my Cobol routine, and this Cobol routine produces the desired result, so all is good. It is the end of the execution, which crashes for reasons I do not understand.

I use GnuCobol version 3.1.2.0. I looked at source code of cob_init() and cob_init_nomain() and didn't find anything scary there. The source code for cob_init*() is here:
https://sourceforge.net/p/gnucobol/code/HEAD/tree/branches/gnucobol-3.x/libcob/common.c

The segmentation fault comes after finishing. It is not cob_init() which crashes. It looks like that the cleanup of PHP crashes.

I ran the same with gdb and valgrind and they both show that getenv() in libc is unhappy:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7615dcd in getenv () from /usr/lib/libc.so.6

Below is the output of valgrind when running the same:
==39625== Invalid read of size 2                                                                                                                                       
==39625==    at 0x50AADCD: getenv (in /usr/lib/libc-2.33.so)                                                                                                           
==39625==    by 0x5A2C3F: ??? (in /usr/bin/php)                                                                                                                        
==39625==    by 0x59C62C: ??? (in /usr/bin/php)                                                                                                                        
==39625==    by 0x5AE5A9: zend_hash_graceful_reverse_destroy (in /usr/bin/php)                                                                                         
==39625==    by 0x59D58C: ??? (in /usr/bin/php)                                                                                                                        
==39625==    by 0x535CE7: ??? (in /usr/bin/php)                                                                                                                        
==39625==    by 0x3444BC: ??? (in /usr/bin/php)
==39625==    by 0x5093B24: (below main) (in /usr/lib/libc-2.33.so)
==39625==  Address 0x86e3028 is not stack'd, malloc'd or (recently) free'd
==39625== 
==39625== Jump to the invalid address stated on the next line
==39625==    at 0x86AFD20: ???
==39625==    by 0x50A8F7F: ??? (in /usr/lib/libc-2.33.so)
==39625==    by 0x50AADCC: getenv (in /usr/lib/libc-2.33.so)
==39625==    by 0x5A2C3F: ??? (in /usr/bin/php)
==39625==    by 0x59C62C: ??? (in /usr/bin/php)
==39625==    by 0x5AE5A9: zend_hash_graceful_reverse_destroy (in /usr/bin/php)
==39625==    by 0x59D58C: ??? (in /usr/bin/php)
==39625==    by 0x535CE7: ??? (in /usr/bin/php)
==39625==    by 0x3444BC: ??? (in /usr/bin/php)
==39625==    by 0x5093B24: (below main) (in /usr/lib/libc-2.33.so)
==39625==  Address 0x86afd20 is not stack'd, malloc'd or (recently) free'd
==39625== 
==39625== 
==39625== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==39625==  Access not within mapped region at address 0x86AFD20
==39625==    at 0x86AFD20: ???
==39625==    by 0x50A8F7F: ??? (in /usr/lib/libc-2.33.so)
==39625==    by 0x50AADCC: getenv (in /usr/lib/libc-2.33.so)
==39625==    by 0x5A2C3F: ??? (in /usr/bin/php)
==39625==    by 0x59C62C: ??? (in /usr/bin/php)
==39625==    by 0x5AE5A9: zend_hash_graceful_reverse_destroy (in /usr/bin/php)
==39625==    by 0x59D58C: ??? (in /usr/bin/php)
==39625==    by 0x535CE7: ??? (in /usr/bin/php)
==39625==    by 0x3444BC: ??? (in /usr/bin/php)
==39625==    by 0x5093B24: (below main) (in /usr/lib/libc-2.33.so)

This is what ldd says about php and libcob:
ldd /bin/php
        linux-vdso.so.1 (0x00007ffd488dc000)
        libargon2.so.1 => /usr/lib/libargon2.so.1 (0x00007f6a0f5d8000)
        libresolv.so.2 => /usr/lib/libresolv.so.2 (0x00007f6a0f5b8000)
        libreadline.so.8 => /usr/lib/libreadline.so.8 (0x00007f6a0f560000)
        libutil.so.1 => /usr/lib/libutil.so.1 (0x00007f6a0f558000)
        libm.so.6 => /usr/lib/libm.so.6 (0x00007f6a0f410000)
        libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f6a0f408000)
        libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f6a0f298000)
        libssl.so.1.1 => /usr/lib/libssl.so.1.1 (0x00007f6a0f200000)
        libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0x00007f6a0ef20000)
        libpcre2-8.so.0 => /usr/lib/libpcre2-8.so.0 (0x00007f6a0ee80000)
        libz.so.1 => /usr/lib/libz.so.1 (0x00007f6a0ee60000)
        libonig.so.5 => /usr/lib/libonig.so.5 (0x00007f6a0edc8000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f6a0ebf8000)
        libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f6a0ebd0000)
        libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007f6a0eb58000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6a10840000)
        libicuuc.so.68 => /usr/lib/libicuuc.so.68 (0x00007f6a0e968000)
        liblzma.so.5 => /usr/lib/liblzma.so.5 (0x00007f6a0e940000)
        libicudata.so.68 => /usr/lib/libicudata.so.68 (0x00007f6a0cdf8000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f6a0cc18000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f6a0cbf8000)

ldd /lib/libcob.so.4
        linux-vdso.so.1 (0x00007ffd33d9c000)
        libm.so.6 => /usr/lib/libm.so.6 (0x00007fcda4c70000)
        libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007fcda4bd0000)
        libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007fcda4a60000)
        libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007fcda49e8000)
        libdb-5.3.so => /usr/lib/libdb-5.3.so (0x00007fcda4828000)
        libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fcda4820000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007fcda4650000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007fcda4e50000)
        libicuuc.so.68 => /usr/lib/libicuuc.so.68 (0x00007fcda4460000)
        libz.so.1 => /usr/lib/libz.so.1 (0x00007fcda4440000)
        liblzma.so.5 => /usr/lib/liblzma.so.5 (0x00007fcda4418000)
        libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fcda43f0000)
        libicudata.so.68 => /usr/lib/libicudata.so.68 (0x00007fcda28a8000)
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007fcda26c8000)
        libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007fcda26a8000)

Many thanks for taking a look at this.

Test script:
---------------
<html>
<title>Call Cobol from PHP via FFI</title>
<body>
<?php
        $cobrt = FFI::cdef("void cob_init_nomain(int,char**); int cob_tidy(void);", "libcob.so.4");

        echo "Before calling cob_init():<br>\n";
        $cobrt->cob_init_nomain(0,null);
?>
</body>
</html>


Expected result:
----------------
<html>
<title>Call Cobol from PHP via FFI</title>
<body>
Before calling cob_init():<br>
</body>
</html>

Actual result:
--------------
<html>
<title>Call Cobol from PHP via FFI</title>
<body>
Before calling cob_init():<br>
</body>
</html>

zsh: segmentation fault (core dumped)  php7 phpsqrt.php


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-03-17 16:13 UTC] cmb@php.net
-PHP Version: 8.0.3 +PHP Version: 7.4.16
 [2021-03-17 16:13 UTC] cmb@php.net
Just guessing: does that also happen if you don't load the intl
extension?
 [2021-03-17 19:45 UTC] Elmar dot Klausmeier at gmail dot com
According my php.ini, the intl extension is not activated -- it is commented out:

;extension=intl
 [2021-03-21 19:57 UTC] Elmar dot Klausmeier at gmail dot com
-Status: Open +Status: Closed
 [2021-03-21 19:57 UTC] Elmar dot Klausmeier at gmail dot com
I built PHP from source with --enable-debug. valgrind showed that

==37350== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==37350==  Access not within mapped region at address 0x852AD20
==37350==    at 0x852AD20: ???
==37350==    by 0x556EF7F: ??? (in /usr/lib/libc-2.33.so)
==37350==    by 0x5570DCC: getenv (in /usr/lib/libc-2.33.so)
==37350==    by 0x76BB43: module_destructor (zend_API.c:2629)
==37350==    by 0x75EE31: module_destructor_zval (zend.c:782)
==37350==    by 0x7777A1: _zend_hash_del_el_ex (zend_hash.c:1330)
==37350==    by 0x777880: _zend_hash_del_el (zend_hash.c:1353)
==37350==    by 0x779188: zend_hash_graceful_reverse_destroy (zend_hash.c:1807)
==37350==    by 0x769390: zend_destroy_modules (zend_API.c:1992)
==37350==    by 0x75F582: zend_shutdown (zend.c:1078)
==37350==    by 0x6C3F17: php_module_shutdown (main.c:2359)
==37350==    by 0x84E46D: main (php_cli.c:1351)
==37350==  If you believe this happened as a result of a stack
==37350==  overflow in your program's main thread (unlikely but
==37350==  possible), you can try to increase the size of the
==37350==  main thread stack using the --main-stacksize= flag.
==37350==  The main thread stack size used in this run was 8388608.
. . .
zsh: segmentation fault (core dumped) . . .

Looking at the code in Zend/zend_API.c:

void module_destructor(zend_module_entry *module) /* {{{ */
{
. . .
        module->module_started=0;
        if (module->type == MODULE_TEMPORARY && module->functions) {
                zend_unregister_functions(module->functions, -1, NULL);
        }

#if HAVE_LIBDL
        if (module->handle && !getenv("ZEND_DONT_UNLOAD_MODULES")) {
                DL_UNLOAD(module->handle);
        }
#endif
}
/* }}} */


I.e., getenv("ZEND_DONT_UNLOAD_MODULES") controls the dlclose() of the module. Running PHP with ZEND_DONT_UNLOAD_MODULES=1 solves problem.

See https://www.phpinternalsbook.com/php7/extensions_design/zend_extensions.html.

I still do not understand why unloading the FFI extension with the printf() or j0() example does not crash.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC