Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SEGV on static build php >= 8.3 with gettext #17735

Closed
crazywhalecc opened this issue Feb 8, 2025 · 6 comments
Closed

SEGV on static build php >= 8.3 with gettext #17735

crazywhalecc opened this issue Feb 8, 2025 · 6 comments

Comments

@crazywhalecc
Copy link

crazywhalecc commented Feb 8, 2025

Description

The following code:

<?php
assert(function_exists('gettext'));
assert(function_exists('bindtextdomain'));
assert(function_exists('textdomain'));

if (!is_dir('locale/en_US/LC_MESSAGES/')) {
    mkdir('locale/en_US/LC_MESSAGES/', 0755, true);
}
if (!file_exists('locale/en_US/LC_MESSAGES/test.mo')) {
    $mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';
    file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode($mo));
}
putenv('LANG=en_US');
setlocale(LC_ALL, 'en_US');

$domain = 'test';
bindtextdomain($domain, 'locale/');
textdomain($domain);

assert(gettext(json_decode('"\u793a\u4f8b"', true)) === 'Example');

Resulted in this output:

[1]    1473892 segmentation fault  buildroot/bin/php src/globals/ext-tests/gettext.php

But I expected this output instead:

// no output, retcode is 0

Additional Information

The PHP build is from source and gettext is statically linked. This bug did not exist 2 months ago and only affected PHP 8.3 and 8.4.

  • Build version: 8.4.3-NTS
  • OS: Debian 12 x86_64
  • Build toolchain: musl-toolchain (ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped)

static-php-cli build command:

# static-php-cli has extension check, at this time it fails when checking for gettext and got segfault.
bin/spc build --build-cli gettext --no-strip

Build detail log:

This is static-php-cli build log that shows which build command used. If more details need, I'd like to show.

$ bin/spc build --build-cli gettext --no-strip   
     _        _   _                 _           
 ___| |_ __ _| |_(_) ___      _ __ | |__  _ __  
/ __| __/ _` | __| |/ __|____| '_ \| '_ \| '_ \ 
\__ \ || (_| | |_| | (_|_____| |_) | | | | |_) |
|___/\__\__,_|\__|_|\___|    | .__/|_| |_| .__/   v2.4.4
                             |_|         |_|    
[01:42:42] [INFO] Build OS:         Linux (x86_64)
[01:42:42] [INFO] Build SAPI:       cli
[01:42:42] [INFO] Extensions (1):   gettext
[01:42:42] [INFO] Libraries (2):    libiconv,gettext
[01:42:42] [INFO] Strip Binaries:   no
[01:42:42] [INFO] Enable ZTS:       no
[01:42:42] [INFO] Config File Path: /usr/local/etc/php
[01:42:42] [INFO] PHP Version:      8.4.1
[01:42:42] [NOTI] Build will start after 2s ...
[01:42:44] [INFO] extracting php-src source to /home/jerry/project/static-php-cli/source/php-src ...
[01:42:45] [INFO] Patched source [php-src] after extracted
[01:42:45] [INFO] extracting micro source to /home/jerry/project/static-php-cli/source/php-src/sapi/micro ...
[01:42:45] [INFO] Patching micro with sapi/micro/patches/static_extensions_win32_84.patch
patching file ext/fileinfo/config.w32
patching file ext/openssl/config.w32
[01:42:45] [INFO] Patching micro with sapi/micro/patches/cli_checks_84.patch
patching file TSRM/tsrm_win32.c
Hunk #1 succeeded at 531 (offset 1 line).
patching file ext/ffi/ffi.c
Hunk #1 succeeded at 5415 (offset 12 lines).
patching file ext/opcache/ZendAccelerator.c
Hunk #1 succeeded at 2842 (offset 20 lines).
Hunk #2 succeeded at 3154 (offset 20 lines).
Hunk #3 succeeded at 3166 (offset 20 lines).
Hunk #4 succeeded at 4716 (offset 31 lines).
patching file ext/pdo_sqlite/pdo_sqlite.c
patching file ext/readline/readline_cli.c
patching file ext/sqlite3/sqlite3.c
patching file ext/standard/php_fopen_wrapper.c
patching file ext/standard/proc_open.c
Hunk #1 succeeded at 1277 (offset 3 lines).
patching file main/main.c
Hunk #1 succeeded at 528 (offset 42 lines).
Hunk #2 succeeded at 1409 (offset 38 lines).
patching file win32/console.c
[01:42:45] [INFO] Patching micro with sapi/micro/patches/disable_huge_page_84.patch
patching file configure.ac
Hunk #1 succeeded at 1063 (offset -64 lines).
[01:42:45] [INFO] Patching micro with sapi/micro/patches/vcruntime140_80.patch
patching file win32/winutil.c
[01:42:45] [INFO] Patching micro with sapi/micro/patches/win32_82.patch
patching file win32/build/confutils.js
Hunk #1 succeeded at 3432 (offset -22 lines).
Hunk #2 succeeded at 3444 (offset -22 lines).
[01:42:45] [INFO] Patching micro with sapi/micro/patches/zend_stream.patch
patching file Zend/zend_stream.c
[01:42:45] [INFO] Patched source [micro] after extracted
[01:42:45] [INFO] Installing required library [pkg-config] from pre-built binaries
[01:42:45] [INFO] extracting pkg-config-x86_64-linux.txz package to /home/jerry/project/static-php-cli/buildroot ...
[01:42:45] [INFO] lib [pkg-config] setup success, took 0.01 s
[01:42:45] [INFO] Installing required library [libiconv] from pre-built binaries
[01:42:45] [INFO] extracting libiconv-x86_64-linux.txz package to /home/jerry/project/static-php-cli/buildroot ...
[01:42:45] [INFO] lib [libiconv] setup success, took 0.01 s
[01:42:45] [INFO] Building required library [gettext]
[01:42:45] [INFO] extracting gettext source to /home/jerry/project/static-php-cli/source/gettext ...
[01:42:45] [INFO] Entering dir: /home/jerry/project/static-php-cli/source/gettext
[01:42:45] [INFO] [EXEC]  ./configure --enable-static --disable-shared --disable-java --disable-c+ --with-included-gettext --with-libiconv-prefix=/home/jerry/project/static-php-cli/buildroot --prefix=/home/jerry/project/static-php-cli/buildroot
[01:43:45] [INFO] [EXEC]  make clean
[01:43:46] [INFO] [EXEC]  make -j16
[01:45:07] [INFO] [EXEC]  make install
[01:45:16] [INFO] lib [gettext] setup success, took 151.01 s
[01:45:16] [INFO] Extension [gettext] patched before buildconf
[01:45:16] [INFO] Entering dir: /home/jerry/project/static-php-cli/source/php-src
[01:45:16] [INFO] [EXEC] ./buildconf --force
[01:45:17] [INFO] Extension [gettext] patched before configure
[01:45:17] [INFO] Entering dir: /home/jerry/project/static-php-cli/source/php-src
[01:45:17] [INFO] gettext is using --with-gettext="/home/jerry/project/static-php-cli/buildroot" 
[01:45:17] [INFO] [EXEC] LD_LIBRARY_PATH=/usr/local/musl/x86_64-linux-musl/lib ./configure --prefix= --with-valgrind=no --enable-shared=no --enable-static=yes --disable-all --disable-cgi --disable-phpdbg --enable-cli --disable-fpm --disable-embed --disable-micro --with-config-file-path=/usr/local/etc/php --with-config-file-scan-dir=/usr/local/etc/php/conf.d --with-gettext="/home/jerry/project/static-php-cli/buildroot" CFLAGS='' CPPFLAGS='-I/home/jerry/project/static-php-cli/buildroot/include' LDFLAGS='-L/home/jerry/project/static-php-cli/buildroot/lib' LIBS='-ldl -lpthread -lm' 
[01:45:25] [INFO] cleaning up
[01:45:25] [INFO] Entering dir: /home/jerry/project/static-php-cli/source/php-src
[01:45:25] [INFO] [EXEC] make clean
[01:45:25] [INFO] building cli
[01:45:25] [INFO] Entering dir: /home/jerry/project/static-php-cli/source/php-src
[01:45:25] [INFO] [EXEC] sed -i "s|//lib|/lib|g" Makefile
[01:45:25] [INFO] [EXEC] $SPC_CMD_PREFIX_PHP_MAKE EXTRA_CFLAGS='-g -O0 -fno-ident -fPIE' EXTRA_LIBS='/home/jerry/project/static-php-cli/buildroot/lib/libintl.a /home/jerry/project/static-php-cli/buildroot/lib/libiconv.a /home/jerry/project/static-php-cli/buildroot/lib/libcharset.a  ' EXTRA_LDFLAGS_PROGRAM='-all-static -Wl,-O1 -pie' cli
[01:45:41] [INFO] Deploying cli file
[01:45:41] [INFO] [EXEC] cp '/home/jerry/project/static-php-cli/source/php-src/sapi/cli/php' '/home/jerry/project/static-php-cli/buildroot/bin/'
[01:45:41] [INFO] running cli sanity check
[01:45:41] [INFO] [EXEC] /home/jerry/project/static-php-cli/buildroot/bin/php -r "echo \"hello\";"
[01:45:41] [INFO] [EXEC] /home/jerry/project/static-php-cli/buildroot/bin/php -r "assert(function_exists('gettext'));assert(function_exists('bindtextdomain'));assert(function_exists('textdomain'));if ("'!'"is_dir('locale/en_US/LC_MESSAGES/')) {    mkdir('locale/en_US/LC_MESSAGES/', 0755, true);}if ("'!'"file_exists('locale/en_US/LC_MESSAGES/test.mo')) {    \$mo = '3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABgAAAFEAAAAXAQAAWAAAAAcAAABwAQAAAQAAAAAAAAAAAAAAAgAAAAAAAAAA56S65L6LAFByb2plY3QtSWQtVmVyc2lvbjogUEFDS0FHRSBWRVJTSU9OClJlcG9ydC1Nc2dpZC1CdWdzLVRvOiAKUE8tUmV2aXNpb24tRGF0ZTogWUVBUi1NTy1EQSBITzpNSStaT05FCkxhc3QtVHJhbnNsYXRvcjogRlVMTCBOQU1FIDxFTUFJTEBBRERSRVNTPgpMYW5ndWFnZS1UZWFtOiBMQU5HVUFHRSA8TExAbGkub3JnPgpMYW5ndWFnZTogCk1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CgBFeGFtcGxlAA==';    file_put_contents('locale/en_US/LC_MESSAGES/test.mo', base64_decode(\$mo));}putenv('LANG=en_US');setlocale(LC_ALL, 'en_US');\$domain = 'test';bindtextdomain(\$domain, 'locale/');textdomain(\$domain);assert(gettext(json_decode('\"\u793a\u4f8b\"', true)) === 'Example');"
Segmentation fault
[01:45:41] [CRIT] Build failed with SPC\exception\RuntimeException: extension gettext failed sanity check
[01:45:41] [CRIT] Please check with --debug option to see more details.

GDB debug log:

$ gdb buildroot/bin/php 
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from buildroot/bin/php...
warning: File "/home/jerry/project/static-php-cli/buildroot/bin/php" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
	add-auto-load-safe-path /home/jerry/project/static-php-cli/buildroot/bin/php
line to your configuration file "/home/jerry/.config/gdb/gdbinit".
To completely disable this security protection add
	set auto-load safe-path /
line to your configuration file "/home/jerry/.config/gdb/gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
	info "(gdb)Auto-loading safe path"
(gdb) run src/globals/ext-tests/gettext.php
Starting program: /home/jerry/project/static-php-cli/buildroot/bin/php src/globals/ext-tests/gettext.php

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x0000000000b18691 in _libintl_rwlock_unlock_multithreaded (lock=0xe2ec60 <_libintl_state_lock>) at glthread/lock.c:494
#2  0x0000000000b149ad in set_binding_values (domainname=<optimized out>, dirnamep=dirnamep@entry=0x7fffffff5428, codesetp=codesetp@entry=0x0, wdirnamep=0x0) at ./bindtextdom.c:370
#3  0x0000000000b14d31 in set_binding_values (wdirnamep=0x0, codesetp=0x0, dirnamep=0x7fffffff5428, domainname=<optimized out>) at ./bindtextdom.c:82
#4  libintl_bindtextdomain (domainname=<optimized out>, dirname=<optimized out>) at ./bindtextdom.c:407
#5  0x000000000050f109 in zif_bindtextdomain (execute_data=0x7ffff7c14200, return_value=0x7fffffff6660) at /home/jerry/project/static-php-cli/source/php-src/ext/gettext/gettext.c:205
#6  0x0000000000a1babf in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER () at /home/jerry/project/static-php-cli/source/php-src/Zend/zend_vm_execute.h:1287
#7  execute_ex (ex=0x7ffff7c14020) at /home/jerry/project/static-php-cli/source/php-src/Zend/zend_vm_execute.h:58804
#8  0x0000000000a386cd in zend_execute (op_array=0x7ffff7c8d000, return_value=0x0) at /home/jerry/project/static-php-cli/source/php-src/Zend/zend_vm_execute.h:64236
#9  0x0000000000b028ff in zend_execute_script (type=8, retval=0x0, file_handle=0x7fffffffdf10) at /home/jerry/project/static-php-cli/source/php-src/Zend/zend.c:1934
#10 0x000000000077510f in php_execute_script_ex (primary_file=0x7fffffffdf10, retval=0x0) at /home/jerry/project/static-php-cli/source/php-src/main/main.c:2575
#11 0x0000000000775235 in php_execute_script (primary_file=0x7fffffffdf10) at /home/jerry/project/static-php-cli/source/php-src/main/main.c:2615
#12 0x0000000000b04745 in do_cli (argc=2, argv=0x7ffff7ff85d0) at /home/jerry/project/static-php-cli/source/php-src/sapi/cli/php_cli.c:935
#13 0x0000000000b057c7 in main (argc=2, argv=0x7ffff7ff85d0) at /home/jerry/project/static-php-cli/source/php-src/sapi/cli/php_cli.c:1310

php.tgz

I'm not sure if it's a problem with PHP or gettext itself, I'll be happy to follow up with static-php-cli if any clues are available.

PHP Version

PHP 8.4.3

Operating System

Debian 12 x86_64

@devnexen
Copy link
Member

devnexen commented Feb 8, 2025

can t reproduce with bare php however having experienced with static-php-cli in the past, I ll have a look at this soon-ish.

@devnexen
Copy link
Member

devnexen commented Feb 8, 2025

Can reproduce with 8.3.16 indeed (static cli version only). Looking.

@devnexen
Copy link
Member

devnexen commented Feb 8, 2025

Spent a bit of time, it only segfault from 8.3.0 indeed, 8.2.27 is fine. I had few questions.

This bug did not exist 2 months ago and only affected PHP 8.3 and 8.4.

What was the setting for the last time it did worked ? e.g. PHP version/static-cli-php version itself? was the musl toolchain the same ?

@crazywhalecc
Copy link
Author

Thank you for your long time. I just compared the code carefully two months ago. The problem may be in the gettext build command.

# 2 month ago
$extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : '';
$extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : '';
shell()->cd($this->source_dir)
    ->setEnv(['CFLAGS' => $this->getLibExtraCFlags(), 'LDFLAGS' => $this->getLibExtraLdFlags(), 'LIBS' => $this->getLibExtraLibs()])
    ->execWithEnv(
        './configure ' .
        '--enable-static ' .
        '--disable-shared ' .
        '--disable-java ' .
        '--disable-c+ ' .
        $extra .
        '--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
        '--prefix=' . BUILD_ROOT_PATH
    )
    ->execWithEnv('make clean')
    ->execWithEnv("make -j{$this->builder->concurrency}")
    ->execWithEnv('make install');
# now
$extra = $this->builder->getLib('ncurses') ? ('--with-libncurses-prefix=' . BUILD_ROOT_PATH . ' ') : '';
$extra .= $this->builder->getLib('libxml2') ? ('--with-libxml2-prefix=' . BUILD_ROOT_PATH . ' ') : '';
shell()->cd($this->source_dir)
    ->setEnv(['CFLAGS' => $this->getLibExtraCFlags(), 'LDFLAGS' => $this->getLibExtraLdFlags(), 'LIBS' => $this->getLibExtraLibs()])
    ->execWithEnv(
        './configure ' .
        '--enable-static ' .
        '--disable-shared ' .
        '--disable-java ' .
        '--disable-c+ ' .
        $extra .
        '--with-included-gettext ' .
        '--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
        '--prefix=' . BUILD_ROOT_PATH
    )
    ->execWithEnv('make clean')
    ->execWithEnv("make -j{$this->builder->concurrency}")
    ->execWithEnv('make install');

The only thing that seems to have changed in the past two months is --with-included-gettext, and the toolchain and other compilation parameters have not changed. After I remove this, it's working.

So the conclusion should be that the built-in GNU gettext is now included, which causes the segmentation fault. In other words, either there is a problem with gettext itself, or I am missing some necessary compilation parameters or patches.

I'll dig a little deeper locally, and let me know if there are any details I've overlooked.

@crazywhalecc
Copy link
Author

crazywhalecc commented Feb 10, 2025

I finally figured it out. In short, libintl/gettext was not built in multithreaded mode correctly.

I ended up compiling the gettext library with --enable-threads=isoc+posix and compiling the NTS version with --disable-threads to keep thread safety consistent with PHP, and there were no problems.

I didn't look up how to build it for official or common Linux distributions though, I can only confirm that this method works for static-php-cli.

@devnexen
Copy link
Member

Good to know !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants