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

Seg fault observed while loading dl with RTLD_DEEPBIND #11291

Closed
aswingit opened this issue May 22, 2023 · 10 comments
Closed

Seg fault observed while loading dl with RTLD_DEEPBIND #11291

aswingit opened this issue May 22, 2023 · 10 comments

Comments

@aswingit
Copy link

Description

Issue:
I encountered a segmentation fault when attempting to load a hello_world module using the dl() function. The seg fault does not occur when I remove the RTLD_DEEPBIND mode in the dlopen() function from zend_portability.h.

Version:
I'm using PHP version 8.1.3.

Setup:
Here's the setup I have:

a) I have a hello world module that is built as a dynamic library.
b) I'm loading the dynamic library using the dl() function in PHP and invoking the hello_world function. However, I'm experiencing a segmentation fault with the following stack trace:

#0 0x00000000 in ?? ()
#1 0xf3d7b672 in zend_vspprintf (pbuf=0xffff9c4c, max_len=0,
#include <php.h>

ZEND_BEGIN_ARG_INFO(arginfo_hello_world, 0)
ZEND_END_ARG_INFO()

static PHP_FUNCTION(hello_world)
{
    TSRMLS_FETCH();

    php_printf("Hello, World!\n");
}

static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, arginfo_hello_world)
    PHP_FE_END
};

zend_module_entry hello_module_entry = {
    STANDARD_MODULE_HEADER,
    "hello",
    hello_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_HELLO
ZEND_TSRMLS_CACHE_EXTERN()
#endif

ZEND_GET_MODULE(hello)

PHP_MINIT_FUNCTION(hello)
{
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    return SUCCESS;
}

PHP_RINIT_FUNCTION(hello)
{
#if defined(COMPILE_DL_HELLO) && defined(ZTS)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif
    return SUCCESS;
}

PHP_RSHUTDOWN_FUNCTION(hello)
{
    return SUCCESS;
}

PHP_MINFO_FUNCTION(hello)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "hello support", "enabled");
    php_info_print_table_end();
}

PHP Version

PHP 8.1.3

Operating System

No response

@iluuu1994
Copy link
Member

The TSRMLS_FETCH() macro is no longer available as of PHP-8.0.

- TSRMLS_FETCH

I'm guessing you're using an outdated header. You may be relying on outdated function signatures, but I'm not sure how that is related to RTLD_DEEPBIND. Could you check if fixing your setup resolves the issue?

@aswingit
Copy link
Author

Thanks for the quick response. The issue occurs even without TSRMLS_FETCH();

While researching I saw a similar discussion.
#10670

@iluuu1994
Copy link
Member

iluuu1994 commented May 23, 2023

@aswingit If you're not using LD_PRELOAD then the issue shouldn't be related. TSRMLS_FETCH() is not necessarily the cause of the crash (the macro was a no-op in 7.4), but it's an indication that you're compiling with the wrong header. Other function signatures might have changed, that would cause a segfault. The stack trace also looks odd, I'm not sure why there are only two stack frames if the error occurs in the module. Are you compiling with the --enable-debug flag? Could you check why your headers are not matching up? You could also try the --enable-address-santizer flag to get a better idea of where the issue occurs.

@aswingit
Copy link
Author

Please see the response below

  • I was not using the macro TSRM_FETCH. It was a typo. With macro compilation fails
php_test.c:8:5: error: implicit declaration of function 'TSRMLS_FETCH' [-Werror=implicit-function-declaration]
TSRMLS_FETCH();
  • enable-debug flag is not used
  • Here is the full gdb stack. Error is occurring at line 219 in zend.c

zend_printf_to_smart_string(&buf, format, ap)
#0 0x00000000 in ?? ()
#1 0xf3d5576d in zend_vspprintf () from ****/lib/libphp-8.1.3.so
#2 0xf3ce2495 in php_printf () from ***/lib/libphp-8.1.3.so
#3 0xf7fd57bf in zif_hello_world (execute_data=, return_value=) at php_test.c:17
#4 0xf78f1132 in ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER () at Zend/zend_vm_execute.h:1463
#5 execute_ex (ex=0xf7213010) at Zend/zend_vm_execute.h:55420
#6 0xf78fa46c in zend_execute (op_array=0xf726b000, return_value=0x0) at Zend/zend_vm_execute.h:59771
#7 0xf78852c6 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at Zend/zend.c:1761
#8 0xf781b578 in php_execute_script (primary_file=0xffffc1e8) at main/main.c:2535
#9 0xf76803da in main (argc=3, argv=0xffffc334) at sapi/cgi/cgi_main.c:2533

  • I have activated the --enable-address-sanitizer flag, but I am not observing any extra logs.
  • Please find below the script
    <?php dl('hello_world.so') hello_world(); ?>

@iluuu1994
Copy link
Member

iluuu1994 commented May 23, 2023

@aswingit Could you enable the debug flag? Otherwise the last frame isn't visible, which is where the crash actually happens. Edit: Hmm, you probably alread are, otherwise zif_hello_world wouldn't be visible.

@iluuu1994
Copy link
Member

Maybe @nielsdos has an idea on what could cause this, especially regarding RTLD_DEEPBIND?

@nielsdos
Copy link
Member

The reason the last frame isn't visible is because the segfault occurs when zend_printf_to_smart_string() is called. zend_printf_to_smart_string is a function pointer, defined as a global variable in zend.c. The address you see at the top frame is probably correct: zend_printf_to_smart_string is NULL, hence the crash.
What deepbind does is it causes the linker to prefer local symbols over already-defined symbols. I think the backtrace hints that the zend_printf_to_smart_string symbol from a shared object is taken instead of php itself, which is buggy behaviour. Of course this is speculation, so I'll take a closer look now and try to replicate the issue on my system.

@nielsdos
Copy link
Member

@aswingit How are you compiling and linking your extension? I find it odd that I see both the CGI SAPI and libphp.so in the backtrace. I think that might be the root cause of your issue. The linker will try to use the symbols from libphp.so instead of what has been setup by the CGI SAPI binary.

@aswingit
Copy link
Author

I am building hello_world.so externally and linking with libphp.so.

g++ -m32 -o hello_world.so -shared -ggdb -ggnu-pubnames -Og php_test.o -L/***/lib -lphp-8.1.3

@nielsdos
Copy link
Member

Okay, that is indeed the problem, so it's not actually a bug in PHP. You're not supposed to compile it like that, because now you're already linking to the PHP library instead of relying on the runtime to link against the SAPI binary. This causes the global symbol conflict you were seeing.

Fortunately it's easy to fix. There's an extension example you can find on GitHub: https://github.com/dstogov/php-extension. You can just replace the relevant C code with your own and use the configuration files in that repo. Then you can take a look at https://www.zend.com/resources/php-extensions/building-and-installing-php-extension to see how to setup the build environment (with phpize) and then how to configure and make your extension.

Hope this helps, if you're still stuck after following the instructions please let us 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

3 participants