-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make var_export/debug_zval_dump check for infinite recursion on the *object* #8046
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
Make var_export/debug_zval_dump check for infinite recursion on the *object* #8046
Conversation
Something I also noticed is that php_json_encode will call zend_std_get_properties through Does it make sense to replace those with GC_PROTECT_RECURSION on the object (Z_OBJ_P), instead of the properties of the object (haven't tried this yet). For collections such as SplFixedArray which implement write_properties, it will:
// Zend/zend_types.h
#define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(Z_OBJ(zval))
#define Z_OBJPROP_P(zval_p) Z_OBJPROP(*(zval_p))
// ext/json/json_encoder.c
static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(val);
HashTable* myht = Z_OBJPROP_P(val);
zval retval, fname;
int return_code;
if (myht && GC_IS_RECURSIVE(myht)) {
encoder->error_code = PHP_JSON_ERROR_RECURSION;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} |
I think the problem is in SplFixedArray again. The following patch should fix the problem. Please check and commit this, if you agree. diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c
index dac31fe8c3..8901c95694 100644
--- a/ext/spl/spl_fixedarray.c
+++ b/ext/spl/spl_fixedarray.c
@@ -210,6 +210,10 @@ static HashTable* spl_fixedarray_object_get_properties(zend_object *obj)
if (!spl_fixedarray_empty(&intern->array)) {
zend_long j = zend_hash_num_elements(ht);
+ if (GC_REFCOUNT(ht) > 1) {
+ intern->std.properties = zend_array_dup(ht);
+ GC_TRY_DELREF(ht);
+ }
for (zend_long i = 0; i < intern->array.size; i++) {
zend_hash_index_update(ht, i, &intern->array.elements[i]);
Z_TRY_ADDREF(intern->array.elements[i]); |
Sorry for the late response, I'd missed that email/notification and I would have tested it earlier if I'd seen it. It introduces new issues in #8079 |
Now this looks much clear to me. |
… the object This test case previously failed with an assertion error in debug builds (for updating an index of the array to set it to the same value it already had) Then repeat the existing check on the return value of `get_properties_for` in case there was a reason for doing it that way. 1. `HT_ASSERT_RC1(ht);` would fail for SplFixedArray and related datastructures. 2. In order for a native datastructure to correctly implement `*get_properties_for` for var_export's cycle detection, it would need to return the exact same array every time prior to this PR. This would prevent SplFixedArray or similar classes from returning a temporary array that: 1. Wouldn't be affected by unexpected mutations from error handlers 2. Could be garbage collected instead. Note that SplFixedArray continues to need to return `object->properties` until php 9.0, when dynamic properties are forbidden. (GitHub wasn't updating the previous PR despite pushing the branch to the right repo earlier, creating a new branch) Closes phpGH-8044
b8f7265
to
e2b96e2
Compare
Summarizing motivations for wanting this:
Thankfully, those are the only remaining issues I'm aware of after the last fix https://github.com/php/php-src/pull/8105/files |
Doing the recursion check on the object makes sense to me. However, shouldn't we be doing it only on the object? Why do we need it on both the object and the HT? |
The previous commit checked for recursion on the object itself. Normally, different objects shouldn't share hash tables.
Mostly, it was a lack of clarity on why the check was done on the object properties in the first place rather than the object itself, and to continue printing
That, and to keep interactions with other recursion protection checks on property tables the same, in case there were any objections to that.
|
Switch the check from the result of
get_properties_for
(the returned hash table of properties) to just checking forinfinite recursion on the object.
In order for a native datastructure to correctly implement
*get_properties_for
for var_export's cycle detection,it would need to return the exact same array every time prior to this PR.
Prior to this commit, the requirements for cycle detection
would prevent SplFixedArray or similar classes from returning a
temporary array that:
Note that SplFixedArray continues to need to return
object->properties
until php 9.0, when dynamic properties are forbidden.(even after php 9.0, because
#[\AllowDynamicProperties]
can be used in subclasses?)Thankfully, those are the only remaining issues I'm aware of after the last fix https://github.com/php/php-src/pull/8105/files
(delayed __destruct of elements after a var_export call on a collection followed by change such as remove()/pop(), extra memory usage on internal data structures such as SPL after a var_export call, poor worst-case performance for cycle detection in var_export and related functions)