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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: Elmar dot Klausmeier at gmail dot com
New email:
PHP Version: OS:

 

 [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: Sun Oct 27 16:01:27 2024 UTC