-
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
Detect misuse of ZEND_MAP_PTR_NEW() #16468
base: master
Are you sure you want to change the base?
Conversation
Zend/zend.c
Outdated
@@ -2011,6 +2020,10 @@ ZEND_API void *zend_map_ptr_new(void) | |||
{ | |||
void **ptr; | |||
|
|||
#if ZEND_DEBUG | |||
ZEND_ASSERT((!startup_done || CG(in_compilation) || CG(map_ptr_locked)) && "Can not allocate map ptrs outside of startup and compilation"); |
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.
The CG(in_compilation)
looks suspicious. Is it necessary for PHP without opcache?
I think, it may be better to use CG(map_ptr_protected)
initialized by false
, then setting it to true
in opcache post_starup and then updated by lock/unlock.
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.
Without opcache ZEND_MAP_PTR_NEW()
is always safe, but I think it's better to implement the same restrictions when opcache is not enabled, so that code that works without opcache also works with it.
The
CG(in_compilation)
looks suspicious
Indeed. I was convinced that compilation occurred while opcache was locked, so this was to handle the non-opcache case, but actually it's not.
I've simplified as you suggested, but I still deny usage of ZEND_MAP_PTR_NEW()
after startup when opcache is not enabled.
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.
Without opcache, map_ptr
buffer belongs to process/thread and it may allocate new slots at any time.
With opcache. map_ptr
buffer is addressed through opcache SHM, so we shouldn't add new slots without opcache lock. I think, protection should be obtained and managed by opcache. In case, we manage it outside opcache (e.g. in zend_alloc_ce_cache()
we just negate the protection. (I may be wrong)
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.
Yes, zend_alloc_ce_cache()
is what originally convinced me that the lock was held during compilation, since it calls ZEND_MAP_PTR_NEW_OFFSET()
. But actually, the conditions in zend_alloc_ce_cache()
ensure that we never call ZEND_MAP_PTR_NEW_OFFSET()
after startup when opcache is enabled (a map ptr is only allocated when the class name is interned and not permanent, which only happens without opcache).
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 still think that the modification of CG(map_ptr_protected)
in zend_alloc_ce_cache()
is a bad decision.
The protection should have a single point of responsibility.
But actually, the conditions in zend_alloc_ce_cache() ensure that we never call ZEND_MAP_PTR_NEW_OFFSET() after startup when opcache is enabled (a map ptr is only allocated when the class name is interned and not permanent, which only happens without opcache).
This is too complex verbal condition and it may be easily violated by inaccurate future modification.
You introduce protection exactly against inaccurate future modifications, so it's better not to make exceptions without a big reason.
ZEND_MAP_PTR_NEW()
is only safe during startup and when the opcache shm is locked, however it's easy to forget about that and to misuse it. Similarly,ZEND_MAP_PTR_NEW_STATIC()
can only be used during startup.Here I add assertions to check that we are only using
ZEND_MAP_PTR_NEW()
andZEND_MAP_PTR_NEW_STATIC()
when it's safe to do so.