Skip to content

Do not use RTLD_DEEPBIND if dlmopen is available #18612

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

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

danog
Copy link
Contributor

@danog danog commented May 21, 2025

This pull request disables usage of RTLD_DEEPBIND if dlmopen is available.

Context:

After careful consideration, I believe this is the best approach, after considering the following alternatives:

  • Making a PR based on Add zend.dlopen_deepbind php.ini directive #11094, disabling deepbind by default via an ini default except when using the embed SAPI, changing the ini value in php_embed_init; this is still SAPI-dependent behavior, which will still cause the same segfaults if jemalloc is used with the embed SAPI.
  • Writing a wrapper for the embed SAPI which dlmopens the real libphp.so within a new namespace (essentially what @dstogov intended with his suggestion, except with RTLD_DEEPBIND the isolation is not propagated, with dlmopen namespaces it is)

I opted for the much cleaner approach of completely disabling RTLD_DEEPBIND if dlmopen with LM_ID_NEWLM is available, leaving to users the resposibility of isolating libphp.so when including it by using dlmopen(LM_ID_NEWLM, "libphp.so", RTLD_LAZY); instead of dlopen("libphp.so", RTLD_LAZY|RTLD_DEEPBIND);.

dlmopen provides full recursive isolation for all symbols both in the opened library, and in libraries opened by that library, avoiding symbol conflict issues even more effectively than RTLD_DEEPBIND, which is not recursive.

On platforms where GNU extensions aren't available (and dlmopen thus isn't available), RTLD_DEEPBIND is left enabled; if equivalent namespace isolation methods are available on other platforms, they can be added with later pull requests if needed.

@danog
Copy link
Contributor Author

danog commented May 28, 2025

Ping? :)

@danog danog requested a review from bukka as a code owner May 29, 2025 17:51
@danog
Copy link
Contributor Author

danog commented May 29, 2025

After some more testing, noticed that since full isolation via dlmopen of libphp.so with LM_ID_NEWLM can completely replace DEEPBIND in the sense that it avoids conflicts even better than DEEPBIND does, it has the side effect that isolation can't be enabled on the user side with the apache sapi, as it explicitly requires apache symbols from the parent scope of the executable (I initially assumed this wasn't the case).

Fixed this by re-enabling deepbind only when the apache SAPI is enabled, checking via a SAPI module property instead of at compile-time, to avoid issues when building multiple SAPIs at the same time (this is not the case in most distros, but is still supported by the build system, so leaving it like this).

Ping @iluuu1994 for a review :)

Copy link
Member

@bukka bukka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it should be also enabled for embed and possibly lightspeed as well.

@danog
Copy link
Contributor Author

danog commented Jun 1, 2025

Re: embed, no, deepbind does not need to be enabled for it to be usabile via dlmopen, as the embed SAPI alone only exports symbols, and only when the apache SAPI is enabled some symbols are imported.

Re: litespeed, its SAPI is built as a standalone executable so it shouldn't need deepbind.

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comments. I think, they should help making the patch more consistent and simple.

@danog danog force-pushed the disable_deepbind_use_dlmopen branch from 3ebc822 to 8292577 Compare June 3, 2025 10:39
Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I think this is a neat approach. Please see my questions/comments

@@ -19,6 +19,7 @@

#include "zend_extensions.h"
#include "zend_system_id.h"
#include "SAPI.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still have this include, but I don't understand why you still need this?

@@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#include "zend.h"
#include "main/SAPI.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add this include? I removed it and it still compiles. Same question for the other SAPI files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still fails here and elsewhere if removed (though not locally on my glibc linux machine either): https://github.com/php/php-src/actions/runs/15554443234/job/43791709784?pr=18612

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand. That's unfortunate.
The portability include should not require the SAPI header.
Going via CG() globals (or alike) is better but I see this had issues on ZTS. Not sure how to fix this at this moment. I may be able to take a look later this week.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't quite see why is requiring the SAPI header such a dealbreaker; granted, it should probably be included by zend_portability.h directly (though that's a bit trickier to do), but even if included separately it shouldn't be an issue IMO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping for tomorrow :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it depends on the SAPI header (and only that one really), maybe ... it should be defined in the SAPI header instead now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it depends on the SAPI header (and only that one really), maybe ... it should be defined in the SAPI header instead now?

I think that's wrong too. The macro abstracts away loading the library on different plaforms, so clearly it belongs to zend_portability.h

I wonder if it isn't even better to have a true process-wide global variable instead? No more trouble with CG(...) in ZTS and no dependency on the SAPI level...

Copy link
Member

@bwoebi bwoebi Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, a new static variable is better than making this part of sapi_module.

Zend/zend_API.c Outdated
@@ -40,6 +40,8 @@
/* these variables are true statics/globals, and have to be mutex'ed on every access */
ZEND_API HashTable module_registry;

bool use_deepbind = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be ZEND_API to use the symbol within DL_LOAD() from extensions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call it also zend_dl_use_deepbind. The reason being that public symbols normally need to be prefixed by zend_ or php_ (depending on where it is) and the "dl" in the prefix makes it clear it's about dynamic loading.

@danog
Copy link
Contributor Author

danog commented Jun 19, 2025

Not sure about the causes of the windows failure, the UNIX build seems to use the same inputs to the linker...

@bwoebi
Copy link
Member

bwoebi commented Jun 19, 2025

@danog Not sure, but I believe these extern declarations also need ZEND_API.

@@ -461,11 +461,14 @@ static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp
return OK;
}

ZEND_API extern bool use_deepbind;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't work if this was wrapped to some functio (e.g. zend_use_deepbind() that would set such variable in zend code). I don't mind the Zend part but it's not exactly nice API for SAPI...

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

Successfully merging this pull request may close these issues.

6 participants