-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Fix GH-12232: FPM: segfault dynamically loading extension without opache #12233
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
Conversation
da22696
to
da3310f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't other calls to zend_interned_strings_activate()
also be wrapped in if (!CG(interned_strings_initialized)) {
? Otherwise another call to it will leak the existing hash table data. It would probably make sense to move the check to zend_interned_strings_activate()
itself. You could probably also drop the global by checking for HT_IS_INITIALIZED
on the hashtable.
Furthermore, there's actually some code that relies on an empty persistent string table to recognize whether we're using opcache.
Lines 1321 to 1323 in 886bf82
if (zend_hash_num_elements(&CG(interned_strings)) > 0) { | |
zend_map_ptr_reset(); | |
} |
We're also calling zend_interned_strings_deactivate()
from php_request_shutdown()
. If we're initializing interned strings before requests are handled, I assume they should survive the request, but they wouldn't and might thus use-after-free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the approach is wrong.
According to back-trace, we are loading extension with type == MODULE_PERSISTENT
, but at the same time we are trying to register its name as an interned string with "request" time using zend_new_interned_string_request()
. zend_new_interned_string_permanent()
should be used at this point. The problem is in FPM tricks.
Ideally fpm_php_init_child()
should be called before switching to request storage. This probably won't work with non-static FPM pools.
It's possible to switch string storage to permanent and back calling zend_interned_strings_switch_storage()
in appropriate places of FPM , but this may cause problems when opcache loaded. In the worst case we will need an API to override and restore interned_string_request_handler
.
@dstogov thanks for the review and suggestions. See below my comments:
You are of course right. This usage is incorrect. It was actually done in this way sooner in FPM and engine changes later probably broke this but no one probably defines extension in FPM config and not using opcache at the same time. I found it only when testing another thing in my debug build. Still, it is a crash so it should be fixed.
Yeah this wouldn't work. The reason is that it needs to load extension in a child is because php_admin_value is a pool configuration. It means it might differ between pools so the only thing how to make it work is to load extension dynamically in each child because pool is just a group of children. I think it is another problem that could be resolved by a pool manager: #11723 as we could do it in that pool manager process before startup in the same way how it is loaded through INI.
That |
@dstogov Just a gentle reminder when you have time to check the above and the new PR out. Thanks. |
This was implemented in #12277 |
The reason for the cache is that loading happens after the child is forked before the interned strings are initialiazed if opcache is not loaded. If opcache is loaded, it overwrites
zend_new_interned_string_request
and uses its own version which does not have this issue.The fix makes sure that the interned string hast table is initialized before used.