diff options
author | Dmitry Stogov <dmitry@zend.com> | 2017-10-06 01:34:50 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2017-10-06 01:34:50 +0300 |
commit | cb9d81ef4f07f82835273800b0cb3d6a67816050 (patch) | |
tree | dad640422674e3eb45a2577f5b29fcd7ad9c8676 /ext | |
parent | 39ea632f7468e1001b15b9c43afc6aba9debdc9c (diff) | |
download | php-git-cb9d81ef4f07f82835273800b0cb3d6a67816050.tar.gz |
Refactored recursion pretection
Diffstat (limited to 'ext')
-rw-r--r-- | ext/filter/filter.c | 6 | ||||
-rw-r--r-- | ext/json/json_encoder.c | 32 | ||||
-rw-r--r-- | ext/mbstring/mbstring.c | 29 | ||||
-rw-r--r-- | ext/mbstring/tests/bug66964.phpt | 15 | ||||
-rw-r--r-- | ext/opcache/zend_accelerator_util_funcs.c | 3 | ||||
-rw-r--r-- | ext/opcache/zend_persist.c | 2 | ||||
-rw-r--r-- | ext/soap/php_encoding.c | 6 | ||||
-rw-r--r-- | ext/spl/spl_observer.c | 11 | ||||
-rw-r--r-- | ext/standard/array.c | 92 | ||||
-rw-r--r-- | ext/standard/http.c | 10 | ||||
-rw-r--r-- | ext/standard/php_array.h | 2 | ||||
-rw-r--r-- | ext/standard/tests/array/compact_variation1.phpt | 4 | ||||
-rw-r--r-- | ext/standard/tests/array/count_variation3.phpt | 2 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/debug_zval_dump_o.phpt | 492 | ||||
-rw-r--r-- | ext/standard/var.c | 87 | ||||
-rw-r--r-- | ext/wddx/wddx.c | 44 | ||||
-rw-r--r-- | ext/xmlrpc/xmlrpc-epi-php.c | 19 |
17 files changed, 197 insertions, 659 deletions
diff --git a/ext/filter/filter.c b/ext/filter/filter.c index cdc5e15bb6..17fc500a9d 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -503,21 +503,21 @@ static void php_zval_filter_recursive(zval *value, zend_long filter, zend_long f if (Z_TYPE_P(value) == IS_ARRAY) { zval *element; - if (Z_ARRVAL_P(value)->u.v.nApplyCount > 1) { + if (Z_IS_RECURSIVE_P(value)) { return; } + Z_PROTECT_RECURSION_P(value); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), element) { ZVAL_DEREF(element); SEPARATE_ZVAL_NOREF(element); if (Z_TYPE_P(element) == IS_ARRAY) { - Z_ARRVAL_P(element)->u.v.nApplyCount++; php_zval_filter_recursive(element, filter, flags, options, charset, copy); - Z_ARRVAL_P(element)->u.v.nApplyCount--; } else { php_zval_filter(element, filter, flags, options, charset, copy); } } ZEND_HASH_FOREACH_END(); + Z_UNPROTECT_RECURSION_P(value); } else { php_zval_filter(value, filter, flags, options, charset, copy); } diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 1fa2344204..2c3843fdd2 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -113,17 +113,17 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options) } /* }}} */ -#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \ +#define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \ do { \ - if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \ - ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \ + if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \ + GC_PROTECT_RECURSION(_tmp_ht); \ } \ } while (0) -#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \ +#define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \ do { \ - if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \ - ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \ + if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \ + GC_UNPROTECT_RECURSION(_tmp_ht); \ } \ } while (0) @@ -140,13 +140,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso r = PHP_JSON_OUTPUT_OBJECT; } - if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) { + if (myht && GC_IS_RECURSIVE(myht)) { encoder->error_code = PHP_JSON_ERROR_RECURSION; smart_str_appendl(buf, "null", 4); return FAILURE; } - PHP_JSON_HASH_APPLY_PROTECTION_INC(myht); + PHP_JSON_HASH_PROTECT_RECURSION(myht); if (r == PHP_JSON_OUTPUT_ARRAY) { smart_str_appendc(buf, '['); @@ -212,13 +212,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso if (php_json_encode_zval(buf, data, options, encoder) == FAILURE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } } ZEND_HASH_FOREACH_END(); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); if (encoder->depth > encoder->max_depth) { encoder->error_code = PHP_JSON_ERROR_DEPTH; @@ -445,7 +445,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op zval retval, fname; int return_code; - if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) { + 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); @@ -453,7 +453,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op return FAILURE; } - PHP_JSON_HASH_APPLY_PROTECTION_INC(myht); + PHP_JSON_HASH_PROTECT_RECURSION(myht); ZVAL_STRING(&fname, "jsonSerialize"); @@ -466,7 +466,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } @@ -478,19 +478,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } if ((Z_TYPE(retval) == IS_OBJECT) && (Z_OBJ(retval) == Z_OBJ_P(val))) { /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */ - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return_code = php_json_encode_array(buf, &retval, options, encoder); } else { /* All other types, encode as normal */ return_code = php_json_encode_zval(buf, &retval, options, encoder); - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); } zval_ptr_dtor(&retval); diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 0bb18e9b57..6d1103aff7 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3129,11 +3129,12 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons return NULL; } - if (input->u.v.nApplyCount++ > 1) { - input->u.v.nApplyCount--; + if (GC_IS_RECURSIVE(input)) { + GC_UNPROTECT_RECURSION(input); php_error_docref(NULL, E_WARNING, "Cannot convert recursively referenced values"); return NULL; } + GC_PROTECT_RECURSION(input); output = zend_new_array(zend_hash_num_elements(input)); ZEND_HASH_FOREACH_KEY_VAL(input, idx, key, entry) { /* convert key */ @@ -3177,7 +3178,7 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons zend_hash_index_add(output, idx, &entry_tmp); } } ZEND_HASH_FOREACH_END(); - input->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(input); return output; } @@ -3764,11 +3765,11 @@ PHP_FUNCTION(mb_convert_variables) if (target_hash != NULL) { while ((hash_entry = zend_hash_get_current_data(target_hash)) != NULL) { if (Z_REFCOUNTED_P(var)) { - if (++target_hash->u.v.nApplyCount > 1) { - --target_hash->u.v.nApplyCount; + if (GC_IS_RECURSIVE(target_hash)) { recursion_error = 1; goto detect_end; } + GC_PROTECT_RECURSION(target_hash); } zend_hash_move_forward(target_hash); if (Z_TYPE_P(hash_entry) == IS_INDIRECT) { @@ -3813,9 +3814,7 @@ detect_end: if (recursion_error) { while(stack_level-- && (var = &stack[stack_level])) { if (Z_REFCOUNTED_P(var)) { - if (HASH_OF(var)->u.v.nApplyCount > 1) { - HASH_OF(var)->u.v.nApplyCount--; - } + Z_UNPROTECT_RECURSION_P(var); } } efree(stack); @@ -3880,11 +3879,11 @@ detect_end: ZVAL_DEREF(hash_entry); if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) { if (Z_REFCOUNTED_P(hash_entry)) { - if (++(HASH_OF(hash_entry)->u.v.nApplyCount) > 1) { - --(HASH_OF(hash_entry)->u.v.nApplyCount); + if (Z_IS_RECURSIVE_P(hash_entry)) { recursion_error = 1; goto conv_end; } + Z_PROTECT_RECURSION_P(hash_entry); } if (stack_level >= stack_max) { stack_max += PHP_MBSTR_STACK_BLOCK_SIZE; @@ -3933,9 +3932,7 @@ conv_end: if (recursion_error) { while(stack_level-- && (var = &stack[stack_level])) { if (Z_REFCOUNTED_P(var)) { - if (HASH_OF(var)->u.v.nApplyCount > 1) { - HASH_OF(var)->u.v.nApplyCount--; - } + Z_UNPROTECT_RECURSION_P(var); } } efree(stack); @@ -4787,12 +4784,12 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str return 0; } - if (vars->u.v.nApplyCount++ > 1) { - vars->u.v.nApplyCount--; + if (GC_IS_RECURSIVE(vars)) { mbfl_buffer_converter_delete(convd); php_error_docref(NULL, E_WARNING, "Cannot not handle circular references"); return 0; } + GC_PROTECT_RECURSION(vars); ZEND_HASH_FOREACH_KEY_VAL(vars, idx, key, entry) { ZVAL_DEREF(entry); if (key) { @@ -4826,7 +4823,7 @@ MBSTRING_API int php_mb_check_encoding_recursive(HashTable *vars, const zend_str break; } } ZEND_HASH_FOREACH_END(); - vars->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(vars); mbfl_buffer_converter_delete(convd); return valid; } diff --git a/ext/mbstring/tests/bug66964.phpt b/ext/mbstring/tests/bug66964.phpt index e982aa2e01..c33bb67c49 100644 --- a/ext/mbstring/tests/bug66964.phpt +++ b/ext/mbstring/tests/bug66964.phpt @@ -49,5 +49,16 @@ array(5) { [3]=> string(21) "日本語テキスト" [4]=> - *RECURSION* -}
\ No newline at end of file + &array(5) { + [0]=> + string(21) "日本語テキスト" + [1]=> + string(21) "日本語テキスト" + [2]=> + string(21) "日本語テキスト" + [3]=> + string(21) "日本語テキスト" + [4]=> + *RECURSION* + } +} diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 930bcceaf2..6926798ab6 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -181,7 +181,7 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source) ht->nNumOfElements = source->nNumOfElements; ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZVAL_PTR_DTOR; - ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION; + ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED); ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX; if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) { @@ -378,7 +378,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce) /* constants table */ zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table); - ce->constants_table.u.flags &= ~HASH_FLAG_APPLY_PROTECTION; /* interfaces aren't really implemented, so we create a new table */ if (ce->num_interfaces) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 2ce4c05ba3..cd4077279f 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -305,7 +305,6 @@ static void zend_persist_zval(zval *z) GC_REFCOUNT(Z_COUNTED_P(z)) = 2; GC_FLAGS(Z_COUNTED_P(z)) |= IS_ARRAY_IMMUTABLE; Z_ARRVAL_P(z)->u.flags |= HASH_FLAG_STATIC_KEYS; - Z_ARRVAL_P(z)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } break; @@ -374,7 +373,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc GC_REFCOUNT(op_array->static_variables) = 2; GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); op_array->static_variables->u.flags |= HASH_FLAG_STATIC_KEYS; - op_array->static_variables->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } } diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index a88d14fb1a..6d69d81c33 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -453,7 +453,7 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml } else { if (check_class_map && SOAP_GLOBAL(class_map) && data && Z_TYPE_P(data) == IS_OBJECT && - !ZEND_HASH_GET_APPLY_COUNT(Z_OBJPROP_P(data))) { + !GC_IS_RECURSIVE(Z_OBJPROP_P(data))) { zend_class_entry *ce = Z_OBJCE_P(data); zval *tmp; zend_string *type_name; @@ -1859,9 +1859,9 @@ static xmlNodePtr to_xml_object(encodeTypePtr type, zval *data, int style, xmlNo sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_LIST && sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_UNION) { - if (prop) ZEND_HASH_INC_APPLY_COUNT(prop); + if (prop) {GC_PROTECT_RECURSION(prop);} xmlParam = master_to_xml(sdlType->encode, data, style, parent); - if (prop) ZEND_HASH_DEC_APPLY_COUNT(prop); + if (prop) {GC_UNPROTECT_RECURSION(prop);} } else { zval rv; zval *tmp = get_zval_property(data, "_", &rv); diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index c6d366515b..e9dc418b53 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -569,12 +569,13 @@ SPL_METHOD(SplObjectStorage, count) } if (mode == COUNT_RECURSIVE) { - zend_long ret = zend_hash_num_elements(&intern->storage); - zval *element; + zend_long ret; - ZEND_HASH_FOREACH_VAL(&intern->storage, element) { - ret += php_count_recursive(element, mode); - } ZEND_HASH_FOREACH_END(); + if (mode != COUNT_RECURSIVE) { + ret = zend_hash_num_elements(&intern->storage); + } else { + ret = php_count_recursive(&intern->storage); + } RETURN_LONG(ret); return; diff --git a/ext/standard/array.c b/ext/standard/array.c index e55924c66b..18a8a2e77f 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -743,30 +743,29 @@ PHP_FUNCTION(ksort) } /* }}} */ -PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */ +PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */ { zend_long cnt = 0; zval *element; - if (Z_TYPE_P(array) == IS_ARRAY) { - if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) { + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(ht)) { php_error_docref(NULL, E_WARNING, "recursion detected"); return 0; } + GC_PROTECT_RECURSION(ht); + } - cnt = zend_array_count(Z_ARRVAL_P(array)); - if (mode == COUNT_RECURSIVE) { - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { - Z_ARRVAL_P(array)->u.v.nApplyCount++; - } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { - ZVAL_DEREF(element); - cnt += php_count_recursive(element, COUNT_RECURSIVE); - } ZEND_HASH_FOREACH_END(); - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { - Z_ARRVAL_P(array)->u.v.nApplyCount--; - } + cnt = zend_array_count(ht); + ZEND_HASH_FOREACH_VAL(ht, element) { + ZVAL_DEREF(element); + if (Z_TYPE_P(element) == IS_ARRAY) { + cnt += php_count_recursive(Z_ARRVAL_P(element)); } + } ZEND_HASH_FOREACH_END(); + + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(ht); } return cnt; @@ -794,12 +793,10 @@ PHP_FUNCTION(count) RETURN_LONG(0); break; case IS_ARRAY: - cnt = zend_array_count(Z_ARRVAL_P(array)); - if (mode == COUNT_RECURSIVE) { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { - ZVAL_DEREF(element); - cnt += php_count_recursive(element, COUNT_RECURSIVE); - } ZEND_HASH_FOREACH_END(); + if (mode != COUNT_RECURSIVE) { + cnt = zend_array_count(Z_ARRVAL_P(array)); + } else { + cnt = php_count_recursive(Z_ARRVAL_P(array)); } RETURN_LONG(cnt); break; @@ -1431,7 +1428,7 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ ZVAL_DEREF(zv); SEPARATE_ARRAY(zv); thash = Z_ARRVAL_P(zv); - if (thash->u.v.nApplyCount > 1) { + if (GC_IS_RECURSIVE(thash)) { php_error_docref(NULL, E_WARNING, "recursion detected"); result = FAILURE; break; @@ -1442,12 +1439,12 @@ static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */ orig_array_walk_fci_cache = BG(array_walk_fci_cache); Z_ADDREF(ref); - thash->u.v.nApplyCount++; + GC_PROTECT_RECURSION(thash); result = php_array_walk(zv, userdata, recursive); if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) { /* If the hashtable changed in the meantime, we'll "leak" this apply count * increment -- our reference to thash is no longer valid. */ - thash->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(thash); } zval_ptr_dtor(&ref); @@ -2577,19 +2574,18 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu } } } else if (Z_TYPE_P(entry) == IS_ARRAY) { - if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) { - php_error_docref(NULL, E_WARNING, "recursion detected"); - return; - } - - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { - Z_ARRVAL_P(entry)->u.v.nApplyCount++; + if (Z_REFCOUNTED_P(entry)) { + if (Z_IS_RECURSIVE_P(entry)) { + php_error_docref(NULL, E_WARNING, "recursion detected"); + return; + } + Z_PROTECT_RECURSION_P(entry); } ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) { php_compact_var(eg_active_symbol_table, return_value, value_ptr); } ZEND_HASH_FOREACH_END(); - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { - Z_ARRVAL_P(entry)->u.v.nApplyCount--; + if (Z_REFCOUNTED_P(entry)) { + Z_UNPROTECT_RECURSION_P(entry); } } } @@ -3636,7 +3632,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */ ZVAL_DEREF(src_zval); ZVAL_DEREF(dest_zval); thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL; - if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { + if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { php_error_docref(NULL, E_WARNING, "recursion detected"); return 0; } @@ -3662,12 +3658,12 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */ src_zval = &tmp; } if (Z_TYPE_P(src_zval) == IS_ARRAY) { - if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { - thash->u.v.nApplyCount++; + if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) { + GC_PROTECT_RECURSION(thash); } ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval)); - if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { - thash->u.v.nApplyCount--; + if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(thash); } if (!ret) { return 0; @@ -3761,8 +3757,8 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ * dest_zval = dest_entry; ZVAL_DEREF(dest_zval); - if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 || - Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 || + if (Z_IS_RECURSIVE_P(dest_zval) || + Z_IS_RECURSIVE_P(src_zval) || (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { php_error_docref(NULL, E_WARNING, "recursion detected"); return 0; @@ -3772,20 +3768,20 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ * SEPARATE_ZVAL(dest_entry); dest_zval = dest_entry; - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { - Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++; + if (Z_REFCOUNTED_P(dest_zval)) { + Z_PROTECT_RECURSION_P(dest_zval); } - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { - Z_ARRVAL_P(src_zval)->u.v.nApplyCount++; + if (Z_REFCOUNTED_P(src_zval)) { + Z_PROTECT_RECURSION_P(src_zval); } ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval)); - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { - Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--; + if (Z_REFCOUNTED_P(dest_zval)) { + Z_UNPROTECT_RECURSION_P(dest_zval); } - if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { - Z_ARRVAL_P(src_zval)->u.v.nApplyCount--; + if (Z_REFCOUNTED_P(src_zval)) { + Z_UNPROTECT_RECURSION_P(src_zval); } if (!ret) { diff --git a/ext/standard/http.c b/ext/standard/http.c index e270342c7b..afbb77d1fa 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -42,7 +42,7 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, return FAILURE; } - if (ht->u.v.nApplyCount > 0) { + if (GC_IS_RECURSIVE(ht)) { /* Prevent recursion */ return SUCCESS; } @@ -136,12 +136,12 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, *(p++) = 'B'; *p = '\0'; } - if (ZEND_HASH_APPLY_PROTECTION(ht)) { - ht->u.v.nApplyCount++; + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_PROTECT_RECURSION(ht); } php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, newprefix, newprefix_len, "%5D", 3, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type); - if (ZEND_HASH_APPLY_PROTECTION(ht)) { - ht->u.v.nApplyCount--; + if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(ht); } efree(newprefix); } else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) { diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 2b58db71e3..d9c5eedc18 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -107,7 +107,7 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src); PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src); PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src); PHPAPI int php_multisort_compare(const void *a, const void *b); -PHPAPI zend_long php_count_recursive(zval *array, zend_long mode); +PHPAPI zend_long php_count_recursive(HashTable *ht); #define PHP_SORT_REGULAR 0 #define PHP_SORT_NUMERIC 1 diff --git a/ext/standard/tests/array/compact_variation1.phpt b/ext/standard/tests/array/compact_variation1.phpt index ea48132857..d2b9fc45df 100644 --- a/ext/standard/tests/array/compact_variation1.phpt +++ b/ext/standard/tests/array/compact_variation1.phpt @@ -37,10 +37,6 @@ array(1) { Warning: compact(): recursion detected in %s on line %d Warning: compact(): recursion detected in %s on line %d - -Warning: compact(): recursion detected in %s on line %d - -Warning: compact(): recursion detected in %s on line %d array(2) { ["a"]=> int(1) diff --git a/ext/standard/tests/array/count_variation3.phpt b/ext/standard/tests/array/count_variation3.phpt index e11b4c2449..9c89bcd16b 100644 --- a/ext/standard/tests/array/count_variation3.phpt +++ b/ext/standard/tests/array/count_variation3.phpt @@ -35,5 +35,5 @@ int(4) -- $mode = 1: -- Warning: count(): recursion detected in %s on line %d -int(12) +int(4) Done diff --git a/ext/standard/tests/general_functions/debug_zval_dump_o.phpt b/ext/standard/tests/general_functions/debug_zval_dump_o.phpt index c06dff556d..e2c84dae9b 100644 --- a/ext/standard/tests/general_functions/debug_zval_dump_o.phpt +++ b/ext/standard/tests/general_functions/debug_zval_dump_o.phpt @@ -135,25 +135,7 @@ object(object_class)#%d (6) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 2 -- object(no_member_class)#%d (0) refcount(%d){ @@ -184,25 +166,7 @@ object(contains_object_class)#%d (9) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } ["class_object2"]=> object(object_class)#%d (6) refcount(%d){ @@ -222,25 +186,7 @@ object(contains_object_class)#%d (9) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } ["class_object3":"contains_object_class":private]=> object(object_class)#%d (6) refcount(%d){ @@ -260,25 +206,7 @@ object(contains_object_class)#%d (9) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } ["class_object4":protected]=> object(object_class)#%d (6) refcount(%d){ @@ -298,195 +226,13 @@ object(contains_object_class)#%d (9) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } ["no_member_class_object"]=> object(no_member_class)#%d (0) refcount(%d){ } ["class_object5"]=> - object(contains_object_class)#%d (9) refcount(%d){ - ["p"]=> - int(30) - ["p1":protected]=> - int(40) - ["p2":"contains_object_class":private]=> - int(50) - ["class_object1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } - } - ["class_object2"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } - } - ["class_object3":"contains_object_class":private]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } - } - ["class_object4":protected]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } - } - ["no_member_class_object"]=> - object(no_member_class)#%d (0) refcount(%d){ - } - ["class_object5"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 4 -- object(object_class)#%d (6) refcount(%d){ @@ -506,25 +252,7 @@ object(object_class)#%d (6) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 5 -- object(object_class)#%d (6) refcount(%d){ @@ -544,25 +272,7 @@ object(object_class)#%d (6) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 6 -- object(no_member_class)#%d (0) refcount(%d){ @@ -587,25 +297,7 @@ object(object_class)#%d (6) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 9 -- object(object_class)#%d (6) refcount(%d){ @@ -625,25 +317,7 @@ object(object_class)#%d (6) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (6) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - } + *RECURSION* } -- Iteration 10 -- int(30) @@ -674,67 +348,7 @@ object(object_class)#%d (7) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - &object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - *RECURSION* - } - ["obj"]=> - *RECURSION* - } - } + *RECURSION* ["obj"]=> &object(object_class)#%d (7) refcount(%d){ ["value1"]=> @@ -753,89 +367,9 @@ object(object_class)#%d (7) refcount(%d){ int(3) } ["object_class1"]=> - object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - &object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - *RECURSION* - } - } + *RECURSION* ["obj"]=> - &object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - &object(object_class)#%d (7) refcount(%d){ - ["value1"]=> - int(5) - ["value2":"object_class":private]=> - int(10) - ["value3":protected]=> - int(20) - ["value4"]=> - int(30) - ["array_var"]=> - array(2) refcount(%d){ - ["key1"]=> - int(1) - ["key2 "]=> - int(3) - } - ["object_class1"]=> - *RECURSION* - ["obj"]=> - *RECURSION* - } - } + *RECURSION* } } Done diff --git a/ext/standard/var.c b/ext/standard/var.c index 0ae7991412..e28afe9177 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -119,10 +119,12 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) { - PUTS("*RECURSION*\n"); - --myht->u.v.nApplyCount; - return; + if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(myht)) { + PUTS("*RECURSION*\n"); + return; + } + GC_PROTECT_RECURSION(myht); } count = zend_array_count(myht); php_printf("%sarray(%d) {\n", COMMON, count); @@ -131,8 +133,8 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { php_array_element_dump(val, num, key, level); } ZEND_HASH_FOREACH_END(); - if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) { - --myht->u.v.nApplyCount; + if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(myht); } if (is_temp) { zend_hash_destroy(myht); @@ -144,11 +146,11 @@ again: PUTS("}\n"); break; case IS_OBJECT: - if (Z_OBJ_APPLY_COUNT_P(struc) > 0) { + if (Z_IS_RECURSIVE_P(struc)) { PUTS("*RECURSION*\n"); return; } - Z_OBJ_INC_APPLY_COUNT_P(struc); + Z_PROTECT_RECURSION_P(struc); myht = Z_OBJDEBUG_P(struc, is_temp); class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); @@ -172,7 +174,7 @@ again: php_printf("%*c", level-1, ' '); } PUTS("}\n"); - Z_OBJ_DEC_APPLY_COUNT_P(struc); + Z_UNPROTECT_RECURSION_P(struc); break; case IS_RESOURCE: { const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc)); @@ -289,18 +291,20 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 1) { - myht->u.v.nApplyCount--; - PUTS("*RECURSION*\n"); - return; + if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(myht)) { + PUTS("*RECURSION*\n"); + return; + } + GC_PROTECT_RECURSION(myht); } count = zend_array_count(myht); php_printf("%sarray(%d) refcount(%u){\n", COMMON, count, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1); ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { zval_array_element_dump(val, index, key, level); } ZEND_HASH_FOREACH_END(); - if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) { - myht->u.v.nApplyCount--; + if (level > 1 && !(GC_FLAGS(myht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(myht); } if (is_temp) { zend_hash_destroy(myht); @@ -314,12 +318,11 @@ again: case IS_OBJECT: myht = Z_OBJDEBUG_P(struc, is_temp); if (myht) { - if (myht->u.v.nApplyCount > 1) { + if (GC_IS_RECURSIVE(myht)) { PUTS("*RECURSION*\n"); return; - } else { - myht->u.v.nApplyCount++; } + GC_PROTECT_RECURSION(myht); } class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc)); @@ -328,7 +331,7 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { zval_object_property_dump(val, index, key, level); } ZEND_HASH_FOREACH_END(); - myht->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(myht); if (is_temp) { zend_hash_destroy(myht); efree(myht); @@ -487,11 +490,13 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) { - myht->u.v.nApplyCount--; - smart_str_appendl(buf, "NULL", 4); - zend_error(E_WARNING, "var_export does not handle circular references"); - return; + if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(myht)) { + smart_str_appendl(buf, "NULL", 4); + zend_error(E_WARNING, "var_export does not handle circular references"); + return; + } + GC_PROTECT_RECURSION(myht); } if (level > 1) { smart_str_appendc(buf, '\n'); @@ -501,8 +506,8 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { php_array_element_export(val, index, key, level, buf); } ZEND_HASH_FOREACH_END(); - if (ZEND_HASH_APPLY_PROTECTION(myht)) { - myht->u.v.nApplyCount--; + if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(myht); } if (level > 1) { buffer_append_spaces(buf, level - 1); @@ -514,12 +519,12 @@ again: case IS_OBJECT: myht = Z_OBJPROP_P(struc); if (myht) { - if (myht->u.v.nApplyCount > 0) { + if (GC_IS_RECURSIVE(myht)) { smart_str_appendl(buf, "NULL", 4); zend_error(E_WARNING, "var_export does not handle circular references"); return; } else { - myht->u.v.nApplyCount++; + GC_PROTECT_RECURSION(myht); } } if (level > 1) { @@ -534,7 +539,7 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { php_object_element_export(val, index, key, level, buf); } ZEND_HASH_FOREACH_END(); - myht->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(myht); } if (level > 1) { buffer_append_spaces(buf, level - 1); @@ -951,18 +956,22 @@ again: /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ - if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)) - || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1) - ) { - smart_str_appendl(buf, "N;", 2); - } else { - if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { - Z_ARRVAL_P(data)->u.v.nApplyCount++; + if (Z_TYPE_P(data) == IS_ARRAY) { + if (Z_TYPE_P(data) == IS_ARRAY + && (UNEXPECTED(Z_IS_RECURSIVE_P(data)) + || UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc)))) { + smart_str_appendl(buf, "N;", 2); + } else { + if (Z_REFCOUNTED_P(data)) { + Z_PROTECT_RECURSION_P(data); + } + php_var_serialize_intern(buf, data, var_hash); + if (Z_REFCOUNTED_P(data)) { + Z_UNPROTECT_RECURSION_P(data); + } } + } else { php_var_serialize_intern(buf, data, var_hash); - if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { - Z_ARRVAL_P(data)->u.v.nApplyCount--; - } } } ZEND_HASH_FOREACH_END(); } diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c index b6af00edd7..556af05112 100644 --- a/ext/wddx/wddx.c +++ b/ext/wddx/wddx.c @@ -640,28 +640,28 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name) case IS_ARRAY: ht = Z_ARRVAL_P(var); - if (ht->u.v.nApplyCount > 1) { - zend_throw_error(NULL, "WDDX doesn't support circular references"); - return; - } - if (ZEND_HASH_APPLY_PROTECTION(ht)) { - ht->u.v.nApplyCount++; + if (Z_REFCOUNTED_P(var)) { + if (GC_IS_RECURSIVE(ht)) { + zend_throw_error(NULL, "WDDX doesn't support circular references"); + return; + } + GC_PROTECT_RECURSION(ht); } php_wddx_serialize_array(packet, var); - if (ZEND_HASH_APPLY_PROTECTION(ht)) { - ht->u.v.nApplyCount--; + if (Z_REFCOUNTED_P(var)) { + GC_UNPROTECT_RECURSION(ht); } break; case IS_OBJECT: ht = Z_OBJPROP_P(var); - if (ht->u.v.nApplyCount > 1) { + if (GC_IS_RECURSIVE(ht)) { zend_throw_error(NULL, "WDDX doesn't support circular references"); return; } - ht->u.v.nApplyCount++; + GC_PROTECT_RECURSION(ht); php_wddx_serialize_object(packet, var); - ht->u.v.nApplyCount--; + GC_UNPROTECT_RECURSION(ht); break; } @@ -691,28 +691,26 @@ static void php_wddx_add_var(wddx_packet *packet, zval *name_var) target_hash = HASH_OF(name_var); - if (is_array && target_hash->u.v.nApplyCount > 1) { - php_error_docref(NULL, E_WARNING, "recursion detected"); - return; - } - if (!Z_REFCOUNTED_P(name_var)) { ZEND_HASH_FOREACH_VAL(target_hash, val) { php_wddx_add_var(packet, val); } ZEND_HASH_FOREACH_END(); } else { - ZEND_HASH_FOREACH_VAL(target_hash, val) { - if (is_array) { - target_hash->u.v.nApplyCount++; + if (is_array) { + if (GC_IS_RECURSIVE(target_hash)) { + php_error_docref(NULL, E_WARNING, "recursion detected"); + return; } - + GC_PROTECT_RECURSION(target_hash); + } + ZEND_HASH_FOREACH_VAL(target_hash, val) { ZVAL_DEREF(val); php_wddx_add_var(packet, val); - if (is_array) { - target_hash->u.v.nApplyCount--; - } } ZEND_HASH_FOREACH_END(); + if (is_array) { + GC_UNPROTECT_RECURSION(target_hash); + } } } } diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index 2b1a642c05..e20b6d6872 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -556,9 +556,12 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep XMLRPC_VECTOR_TYPE vtype; ht = HASH_OF(&val); - if (ht && ht->u.v.nApplyCount > 1) { - zend_throw_error(NULL, "XML-RPC doesn't support circular references"); - return NULL; + if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) { + if (GC_IS_RECURSIVE(ht)) { + zend_throw_error(NULL, "XML-RPC doesn't support circular references"); + return NULL; + } + GC_PROTECT_RECURSION(ht); } ZVAL_COPY(&val_arr, &val); @@ -569,10 +572,6 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(val_arr), num_index, my_key, pIter) { ZVAL_DEREF(pIter); - ht = HASH_OF(pIter); - if (ht) { - ht->u.v.nApplyCount++; - } if (my_key == NULL) { char *num_str = NULL; @@ -587,10 +586,10 @@ static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int dep } else { XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(ZSTR_VAL(my_key), pIter, depth++)); } - if (ht) { - ht->u.v.nApplyCount--; - } } ZEND_HASH_FOREACH_END(); + if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) { + GC_UNPROTECT_RECURSION(ht); + } zval_ptr_dtor(&val_arr); } break; |