-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
Php8.1 with alternative malloc allocators #10670
Comments
To clarify further I am using docker container and cron.php is for Nextcloud though same error occurs just writing “php” alone. |
Executing php without extensions does not trigger the bug.
I'll now spend some time creating a debug build of PHP with these extensions to track down the bug. Backtrace for curl:
We can see that the freeing happens with the libc allocator indeed instead of snmalloc. So when the extension gets loaded the LD_PRELOAD isn't respected. |
I identified the problem. |
Removing RTLD_DEEPBIND from the DL_LOAD macro in Zend/zend_portability.h fixes this issue for me. |
The flag was introduced by commit 601140cbe9a so I don't think we can easily get rid of it. |
Maybe you can try the follow command to test it. export USE_ZEND_ALLOC=0 && LD_PRELOAD=/your-path/libsnmallocshim.so
php -v |
That won't work AFAIK, because it does not influence the dlopen flag. So that means the LD_PRELOAD has no effect on the extension and hence the libc allocator will be used instead of snmalloc. |
I believe I have fixed snmalloc to interact correctly with @nielsdos if you still have the set up you were using to debug this issues, would you be able to check the latest |
@mjp41 thanks for looking into this. I checked but it does not seem to fix the issue. I still get the invalid free message, and looking at the gdb backtrace it still calls free in libc.so.6 |
It actually looks like those hooks were deprecated and maybe it does not work because they are already removed in the glibc version I use (didnt check this) |
You re right they are, the Mesh folks have been removed those. |
This explains why some people observed jemalloc working and some didn't on the mimalloc issue. @devnexen I don't think jemalloc is seeing the deprecation warning that Mesh had due to providing its own prototypes rather than include @interwq, @davidtgoldblatt, FYI: As glibc is updated the current approach you use to RTLD_DEEPBIND is going to stop working. |
Rebuilding php without the |
Thanks for confirming this. I don't really know what we should do with this issue. It's not really a bug in PHP, but rather a limitation in one of the ways alternative allocators inject themselves into applications. |
The introducing commit 601140cbe9a from 2005 mentions API constraints, but not whether they origin from PHP itself or third party libraries. Maybe @notroj remembers? I would suggest adding a new php.ini directive, which controls what dlopen(3) flag to use, with a default of use |
The problem was extensions loading third-party libraries which used common names, IIRC there were a couple of common libraries using global symbols like "hash_create" or something like that? I can't remember exactly which ones caused me to submit the PHP change, and my mail archive doesn't go back that far either - sorry. We still see this kind of issue occasionally on still-supported systems (not just 2005-era distributions!), but switching to using php-fpm has helped since it isolates PHP-loaded libraries from httpd-loaded libraries.
I'd agree with this, we patch httpd on RHEL to allow doing loading modules using RTLD_DEEPBIND if an environment variable is set. |
Initial draft: (I am unsure whether the flag belongs in From: =?utf-8?q?Christian_G=C3=B6ttsche?= <[email protected]>
Date: Thu, 13 Apr 2023 12:28:34 +0200
Subject: Add zend.dlopen_deepbind php.ini directive
---
Zend/zend.c | 1 +
Zend/zend_globals.h | 2 ++
Zend/zend_portability.h | 2 +-
php.ini-development | 6 ++++++
php.ini-production | 6 ++++++
5 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/Zend/zend.c b/Zend/zend.c
index 33e1c4d..68fdfb2 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -194,6 +194,7 @@ static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */
ZEND_INI_BEGIN()
ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
STD_ZEND_INI_ENTRY("zend.assertions", "1", ZEND_INI_ALL, OnUpdateAssertions, assertions, zend_executor_globals, executor_globals)
+ STD_ZEND_INI_BOOLEAN("zend.dlopen_deepbind", "1", ZEND_INI_SYSTEM, OnUpdateBool, dlopen_deepbind, zend_compiler_globals, compiler_globals)
ZEND_INI_ENTRY3_EX("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, NULL, NULL, NULL, zend_gc_enabled_displayer_cb)
STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals)
ZEND_INI_ENTRY("zend.script_encoding", NULL, ZEND_INI_ALL, OnUpdateScriptEncoding)
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index 368127b..f1a9e02 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -92,6 +92,8 @@ struct _zend_compiler_globals {
bool ini_parser_unbuffered_errors;
+ bool dlopen_deepbind;
+
zend_llist open_files;
struct _zend_ini_parser_param *ini_parser_param;
diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h
index d59fc6d..3bea2d7 100644
--- a/Zend/zend_portability.h
+++ b/Zend/zend_portability.h
@@ -153,7 +153,7 @@
# if defined(RTLD_GROUP) && defined(RTLD_WORLD) && defined(RTLD_PARENT)
# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | RTLD_GROUP | RTLD_WORLD | RTLD_PARENT)
# elif defined(RTLD_DEEPBIND) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(memory_sanitizer)
-# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | RTLD_DEEPBIND)
+# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | (CG(dlopen_deepbind) ? RTLD_DEEPBIND : 0))
# else
# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL)
# endif
diff --git a/php.ini-development b/php.ini-development
index 1565f6b..2239d92 100644
--- a/php.ini-development
+++ b/php.ini-development
@@ -392,6 +392,12 @@ zend.exception_ignore_args = Off
; Production Value: 0
zend.exception_string_param_max_len = 15
+; Decides whether to use the dlopen(3) flag RTLD_DEEPBIND, if availalable, when
+; loading shared libraries. This ensures symbol lookup will prefer symbols from
+; the shared object itself over global ones. Conflicts with the use of custom
+; memory allocators.
+zend.dlopen_deepbind = On
+
;;;;;;;;;;;;;;;;;
; Miscellaneous ;
;;;;;;;;;;;;;;;;;
diff --git a/php.ini-production b/php.ini-production
index 6cb3f1a..ada04d6 100644
--- a/php.ini-production
+++ b/php.ini-production
@@ -388,6 +388,12 @@ zend.exception_ignore_args = On
; of sensitive information in stack traces.
zend.exception_string_param_max_len = 0
+; Decides whether to use the dlopen(3) flag RTLD_DEEPBIND, if availalable, when
+; loading shared libraries. This ensures symbol lookup will prefer symbols from
+; the shared object itself over global ones. Conflicts with the use of custom
+; memory allocators.
+zend.dlopen_deepbind = On
+
;;;;;;;;;;;;;;;;;
; Miscellaneous ;
;;;;;;;;;;;;;;;;; |
Add a runtime configuration option to control whether loading shared libraries via dlopen(3) uses the GNU extension flag RTLD_DEEPBIND, if available. The flag ensures symbol lookup will prefer symbols from the shared object itself over global ones, which can resolve symbol namespace collisions in third party dependencies, see 601140c ("New versions of glibc support a RTLD_DEEPBIND flag to dlopen. The"). However using this flag symbols from preloaded libraries might be de- prioritized, e.g. free(3) from a preloaded allocator by the standard libc. This results in one allocator allocating memory and another one deallocating it, triggering double/invalid-free assertions. Closes: php#10670
Description
The following code:
LD_PRELOAD=libsnmallocshim.so php cron.php
Resulted in this output:
But I expected this output instead:
more info is here:
microsoft/snmalloc#595
microsoft/mimalloc#377
I’m not an allocator expert but seems valgrind is showing memory leaks with alternative allocators and free is being called by malloc when replaced with LD_PRELOAD
PHP Version
PHP 8.1
Operating System
Debian Bullseye
The text was updated successfully, but these errors were encountered: