-
Notifications
You must be signed in to change notification settings - Fork 8k
Fix GH-20370: forbid user stream filters to violate typed property constraints #20373
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
base: PHP-8.3
Are you sure you want to change the base?
Fix GH-20370: forbid user stream filters to violate typed property constraints #20373
Conversation
b0808ce to
786ad32
Compare
786ad32 to
2ad8c64
Compare
ndossche
left a comment
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.
On a phone , so can't check. But you're looking at the property info now, however dynamic properties don't have any. So that means that a dynamically created property $stream would've been overwritten before this patch but now no longer. So that is an unintended behaviour change.
|
Oh alright, I wasn't aware of this subtlety of dynamic properties. Let's refine that. |
|
Hmm so that would require to someone create the dynamic property in filter first and it could then be used in subsequent calls for stream, right? If so, that sounds like a proper edge case... :) |
|
The easiest way to test this is to create a onCreate method that sets a dynamic property on $this |
2ad8c64 to
fe4961a
Compare
|
This will work, except for a pre-existing bug: It would also be great to try to avoid OBJPROP so that the property table isn't rebuilt, but that isn't critical. |
|
I tried to implement what you have in mind in 04e0955 |
7208c60 to
04e0955
Compare
ext/standard/user_filters.c
Outdated
| if (prop_info) { | ||
| /* Declared property (may be uninitialized typed property): always update */ | ||
| zval stream_zval; | ||
| php_stream_to_zval(stream, &stream_zval); | ||
| zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1, &stream_zval); | ||
| } else { | ||
| /* Check if it's a dynamic property */ | ||
| zval *stream_prop = zend_hash_str_find(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1); | ||
| if (stream_prop) { | ||
| zval stream_zval; | ||
| php_stream_to_zval(stream, &stream_zval); | ||
| zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), "stream", sizeof("stream")-1, &stream_zval); | ||
| } |
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 existing behavior skips magic methods and hooks, in addition to ignoring types and readonly. The updated code doesn't.
I think we should consider this an existing bug, so the update is fine.
In this case, we should also use obj->handlers->has_property() to check for property existence, and simplify to something like this:
const zend_class_entry *old_scope = EG(fake_scope);
EG(fake_scope) = Z_OBJCE_P(obj);
zend_string *name = zend_string_init("stream", strlen("stream"));
if (Z_OBJHANDLER_P(obj, has_property)(obj, name, ZEND_PROPERTY_EXISTS)) {
zval stream_zval;
php_stream_to_zval(stream, &stream_zval);
zend_update_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, &stream_zval);
}
EG(fake_scope) = old_scope;
This way, we have less chances of diverging from normal behavior.
We should also check for exception after this.
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.
Thanks for the details! I tried to implement in 3d5358f
ext/standard/user_filters.c
Outdated
| if (stream_prop) { | ||
| /* Use direct manipulation to avoid type checking during cleanup. | ||
| * We need to break the resource reference regardless of property type constraints. */ | ||
| convert_to_null(stream_prop); |
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.
This needs to be changed as well, as setting a typed property to null may be incorrect. We should use Z_OBJHANDLER_P(obj, unset_property).
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.
Is there a reason you could not use unset_property here?
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.
Did the updates iteratively and thought I could push not so long after the first one, but actually I'm struggling a bit to implement it here. Still on it 🙂
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.
Hmm, I wonder if it should be really used. It works on first call, then the property is fully uninitialized. Calling another stream operation breaks because of this (and many tests break when trying to implement it). The current approach "maintains" the property by just setting it to null. I guess we may keep it like this, unless I missed the point?
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.
Ah ok, I was asking because you had marked this as resolved :)
Right, unsetting will not work. Thinking more about it, as the property was assigned with a resource above, it implies that the property has no type hint (or it's mixed). So we can simply assign null here, after all (with the same method as above).
04e0955 to
3d5358f
Compare
Use
zend_update_property()instead of doing things "manually".