Refactored recursion pretection

This commit is contained in:
Dmitry Stogov 2017-10-06 01:34:50 +03:00
parent 39ea632f74
commit cb9d81ef4f
31 changed files with 327 additions and 822 deletions

View File

@ -55,6 +55,9 @@ BCMath:
5. Changed Functions
========================================
. debug_zval_dump() was changed to display recursive arrays and objects
in the same way as var_dump(). Now, it doesn't display them twice.
========================================
6. New Functions
========================================

View File

@ -11,6 +11,7 @@ PHP 7.2 INTERNALS UPGRADE NOTES
h. valid_symbol_table removed
i. array_init() and array_init_size()
j. Run-time constant operand addressing
k. Array/Object recursion protection
2. Build system changes
a. Unix build system changes
@ -70,6 +71,19 @@ PHP 7.2 INTERNALS UPGRADE NOTES
RT_CONSTANT_EX, EX_CONSTANT should be substituted by RT_CONSTANT than now
use "opline" (instead of "op_array") as first argument.
k. Protection from recursion during processing circular data structures was
refactored. HashTable.nApplyCount and IS_OBJ_APPLY_COUNT are replaced by
single flag GC_PROTECTED. Corresponding macros Z_OBJ_APPLY_COUNT,
Z_OBJ_INC_APPLY_COUNT, Z_OBJ_DEC_APPLY_COUNT, ZEND_HASH_GET_APPLY_COUNT,
ZEND_HASH_INC_APPLY_COUNT, ZEND_HASH_DEC_APPLY_COUNT are replaced with
GC_IS_RECURSIVE, GC_PROTECT_RECURSION, GC_UNPROTECT_RECURSION,
Z_IS_RECURSIVE, Z_PROTECT_RECURSION, Z_UNPROTECT_RECURSION.
HASH_FLAG_APPLY_PROTECTION flag and ZEND_HASH_APPLY_PROTECTION() macro
are removed. All mutable arrays should use recursion protection.
Corresponding checks should be replaced by Z_REFCOUNTED() or
!(GC_GLAGS(p) & GC_IMMUTABLE).
========================
2. Build system changes

View File

@ -18,10 +18,7 @@ array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
*RECURSION*
}
*RECURSION*
}
}
array(1) refcount(%d){
@ -30,13 +27,7 @@ array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
array(1) refcount(%d){
[0]=>
*RECURSION*
}
}
*RECURSION*
}
}
}

View File

@ -334,16 +334,17 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
switch (Z_TYPE_P(expr)) {
case IS_ARRAY:
ZEND_PUTS("Array (");
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
ZEND_PUTS(" *RECURSION*");
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
return;
if (Z_REFCOUNTED_P(expr)) {
if (Z_IS_RECURSIVE_P(expr)) {
ZEND_PUTS(" *RECURSION*");
return;
}
Z_PROTECT_RECURSION_P(expr);
}
print_flat_hash(Z_ARRVAL_P(expr));
ZEND_PUTS(")");
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
if (Z_REFCOUNTED_P(expr)) {
Z_UNPROTECT_RECURSION_P(expr);
}
break;
case IS_OBJECT:
@ -353,7 +354,7 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
zend_printf("%s Object (", ZSTR_VAL(class_name));
zend_string_release(class_name);
if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
if (Z_IS_RECURSIVE_P(expr)) {
ZEND_PUTS(" *RECURSION*");
return;
}
@ -362,9 +363,9 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
properties = Z_OBJPROP_P(expr);
}
if (properties) {
Z_OBJ_INC_APPLY_COUNT_P(expr);
Z_PROTECT_RECURSION_P(expr);
print_flat_hash(properties);
Z_OBJ_DEC_APPLY_COUNT_P(expr);
Z_UNPROTECT_RECURSION_P(expr);
}
ZEND_PUTS(")");
break;
@ -384,15 +385,16 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
switch (Z_TYPE_P(expr)) {
case IS_ARRAY:
smart_str_appends(buf, "Array\n");
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) &&
++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) {
smart_str_appends(buf, " *RECURSION*");
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
return;
if (Z_REFCOUNTED_P(expr)) {
if (Z_IS_RECURSIVE_P(expr)) {
smart_str_appends(buf, " *RECURSION*");
return;
}
Z_PROTECT_RECURSION_P(expr);
}
print_hash(buf, Z_ARRVAL_P(expr), indent, 0);
if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) {
Z_ARRVAL_P(expr)->u.v.nApplyCount--;
if (Z_REFCOUNTED_P(expr)) {
Z_UNPROTECT_RECURSION_P(expr);
}
break;
case IS_OBJECT:
@ -405,7 +407,7 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
zend_string_release(class_name);
smart_str_appends(buf, " Object\n");
if (Z_OBJ_APPLY_COUNT_P(expr) > 0) {
if (Z_IS_RECURSIVE_P(expr)) {
smart_str_appends(buf, " *RECURSION*");
return;
}
@ -413,9 +415,9 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
break;
}
Z_OBJ_INC_APPLY_COUNT_P(expr);
Z_PROTECT_RECURSION_P(expr);
print_hash(buf, properties, indent, 1);
Z_OBJ_DEC_APPLY_COUNT_P(expr);
Z_UNPROTECT_RECURSION_P(expr);
if (is_temp) {
zend_hash_destroy(properties);

View File

@ -727,13 +727,13 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
int ret = 1;
zval *val;
ht->u.v.nApplyCount++;
GC_PROTECT_RECURSION(ht);
ZEND_HASH_FOREACH_VAL_IND(ht, val) {
ZVAL_DEREF(val);
if (Z_REFCOUNTED_P(val)) {
if (Z_TYPE_P(val) == IS_ARRAY) {
if (Z_REFCOUNTED_P(val)) {
if (Z_ARRVAL_P(val)->u.v.nApplyCount > 0) {
if (Z_IS_RECURSIVE_P(val)) {
zend_error(E_WARNING, "Constants cannot be recursive arrays");
ret = 0;
break;
@ -749,7 +749,7 @@ static int validate_constant_array(HashTable *ht) /* {{{ */
}
}
} ZEND_HASH_FOREACH_END();
ht->u.v.nApplyCount--;
GC_UNPROTECT_RECURSION(ht);
return ret;
}
/* }}} */
@ -1137,7 +1137,7 @@ ZEND_FUNCTION(get_object_vars)
zobj = Z_OBJ_P(obj);
if (!zobj->ce->default_properties_count && properties == zobj->properties && !ZEND_HASH_GET_APPLY_COUNT(properties)) {
if (!zobj->ce->default_properties_count && properties == zobj->properties && !GC_IS_RECURSIVE(properties)) {
/* fast copy */
if (EXPECTED(zobj->handlers == &std_object_handlers)) {
RETURN_ARR(zend_proptable_to_symtable(properties, 0));

View File

@ -711,9 +711,9 @@ ZEND_METHOD(exception, __toString)
zend_string_release(file);
zval_ptr_dtor(&trace);
Z_OBJPROP_P(exception)->u.v.nApplyCount++;
Z_PROTECT_RECURSION_P(exception);
exception = GET_PROPERTY(exception, ZEND_STR_PREVIOUS);
if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_OBJPROP_P(exception)->u.v.nApplyCount > 0) {
if (exception && Z_TYPE_P(exception) == IS_OBJECT && Z_IS_RECURSIVE_P(exception)) {
break;
}
}
@ -722,8 +722,8 @@ ZEND_METHOD(exception, __toString)
exception = getThis();
/* Reset apply counts */
while (exception && Z_TYPE_P(exception) == IS_OBJECT && (base_ce = i_get_exception_base(exception)) && instanceof_function(Z_OBJCE_P(exception), base_ce)) {
if (Z_OBJPROP_P(exception)->u.v.nApplyCount) {
Z_OBJPROP_P(exception)->u.v.nApplyCount--;
if (Z_IS_RECURSIVE_P(exception)) {
Z_UNPROTECT_RECURSION_P(exception);
} else {
break;
}

View File

@ -72,19 +72,6 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
#define SET_INCONSISTENT(n)
#endif
#define HASH_PROTECT_RECURSION(ht) \
if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
if (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >= (3 << ZEND_HASH_APPLY_SHIFT)) {\
zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");\
} \
ZEND_HASH_INC_APPLY_COUNT(ht); \
}
#define HASH_UNPROTECT_RECURSION(ht) \
if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \
ZEND_HASH_DEC_APPLY_COUNT(ht); \
}
#define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
if ((ht)->nNumUsed >= (ht)->nTableSize) { \
zend_hash_do_resize(ht); \
@ -170,11 +157,11 @@ static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
{HT_INVALID_IDX, HT_INVALID_IDX};
static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
{
GC_REFCOUNT(ht) = 1;
GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | (bApplyProtection ? HASH_FLAG_APPLY_PROTECTION : 0) | HASH_FLAG_STATIC_KEYS;
ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_STATIC_KEYS;
ht->nTableMask = HT_MIN_MASK;
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
ht->nNumUsed = 0;
@ -187,13 +174,13 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
{
_zend_hash_init_int(ht, nSize, pDestructor, persistent, 1);
_zend_hash_init_int(ht, nSize, pDestructor, persistent);
}
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize ZEND_FILE_LINE_DC)
{
HashTable *ht = emalloc(sizeof(HashTable));
_zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0, 1);
_zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, 0);
return ht;
}
@ -245,11 +232,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
pefree(old_data, (ht)->u.flags & HASH_FLAG_PERSISTENT);
}
ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
{
_zend_hash_init_int(ht, nSize, pDestructor, persistent, bApplyProtection);
}
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed)
{
HT_ASSERT_RC1(ht);
@ -317,15 +299,6 @@ ZEND_API uint32_t zend_array_count(HashTable *ht)
}
/* }}} */
ZEND_API void ZEND_FASTCALL zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
{
if (bApplyProtection) {
ht->u.flags |= HASH_FLAG_APPLY_PROTECTION;
} else {
ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION;
}
}
ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
{
HashTableIterator *iter = EG(ht_iterators);
@ -1508,7 +1481,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@ -1522,7 +1494,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_fu
break;
}
}
HASH_UNPROTECT_RECURSION(ht);
}
@ -1534,7 +1505,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@ -1548,7 +1518,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_f
break;
}
}
HASH_UNPROTECT_RECURSION(ht);
}
@ -1562,8 +1531,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
for (idx = 0; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
@ -1583,8 +1550,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_
}
va_end(args);
}
HASH_UNPROTECT_RECURSION(ht);
}
@ -1596,7 +1561,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
idx = ht->nNumUsed;
while (idx > 0) {
idx--;
@ -1613,7 +1577,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
break;
}
}
HASH_UNPROTECT_RECURSION(ht);
}
@ -1776,7 +1739,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->pDestructor = source->pDestructor;
if (source->nNumUsed == 0) {
target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT)) | HASH_FLAG_STATIC_KEYS;
target->nTableMask = HT_MIN_MASK;
target->nNumUsed = 0;
target->nNumOfElements = 0;
@ -1784,7 +1747,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = HT_INVALID_IDX;
HT_SET_DATA_ADDR(target, &uninitialized_bucket);
} else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) {
target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNumUsed = source->nNumUsed;
target->nNumOfElements = source->nNumOfElements;
@ -1801,7 +1764,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = idx;
}
} else if (source->u.flags & HASH_FLAG_PACKED) {
target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNumUsed = source->nNumUsed;
target->nNumOfElements = source->nNumOfElements;
@ -1824,7 +1787,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
target->nInternalPointer = idx;
}
} else {
target->u.flags = (source->u.flags & ~(HASH_FLAG_PERSISTENT|ZEND_HASH_APPLY_COUNT_MASK)) | HASH_FLAG_APPLY_PROTECTION;
target->u.flags = source->u.flags & ~HASH_FLAG_PERSISTENT;
target->nTableMask = source->nTableMask;
target->nNextFreeElement = source->nNextFreeElement;
target->nInternalPointer = source->nInternalPointer;
@ -2402,11 +2365,28 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
IS_CONSISTENT(ht1);
IS_CONSISTENT(ht2);
HASH_PROTECT_RECURSION(ht1);
HASH_PROTECT_RECURSION(ht2);
if (ht1 == ht2) {
return 0;
}
/* use bitwise OR to make only one conditional jump */
if (UNEXPECTED(GC_IS_RECURSIVE(ht1) | GC_IS_RECURSIVE(ht2))) {
zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
}
if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
GC_PROTECT_RECURSION(ht1);
}
if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
GC_PROTECT_RECURSION(ht2);
}
result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
HASH_UNPROTECT_RECURSION(ht1);
HASH_UNPROTECT_RECURSION(ht2);
if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
GC_UNPROTECT_RECURSION(ht1);
}
if (!(GC_FLAGS(ht2) & GC_IMMUTABLE)) {
GC_UNPROTECT_RECURSION(ht2);
}
return result;
}

View File

@ -36,7 +36,6 @@
#define HASH_ADD_NEXT (1<<4)
#define HASH_FLAG_PERSISTENT (1<<0)
#define HASH_FLAG_APPLY_PROTECTION (1<<1)
#define HASH_FLAG_PACKED (1<<2)
#define HASH_FLAG_INITIALIZED (1<<3)
#define HASH_FLAG_STATIC_KEYS (1<<4) /* long and interned strings */
@ -69,14 +68,13 @@ BEGIN_EXTERN_C()
/* startup/shutdown */
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht);
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht);
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
_zend_hash_init((ht), (nSize), (pDestructor), (persistent))
#define zend_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection) \
_zend_hash_init_ex((ht), (nSize), (pDestructor), (persistent), (bApplyProtection))
_zend_hash_init((ht), (nSize), (pDestructor), (persistent))
ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed);
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht);
@ -985,16 +983,6 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
_key = _p->key; \
_val = _z;
#define ZEND_HASH_APPLY_PROTECTION(ht) \
((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION)
#define ZEND_HASH_APPLY_SHIFT 8
#define ZEND_HASH_APPLY_COUNT_MASK 0xff00
#define ZEND_HASH_GET_APPLY_COUNT(ht) (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >> ZEND_HASH_APPLY_SHIFT)
#define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->u.flags += (1 << ZEND_HASH_APPLY_SHIFT))
#define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT))
/* The following macros are useful to insert a sequence of new elements
* of packed array. They may be use insted of series of
* zend_hash_next_index_insert_new()

View File

@ -44,18 +44,6 @@
#define IN_UNSET (1<<2)
#define IN_ISSET (1<<3)
#define Z_OBJ_PROTECT_RECURSION(zval_p) \
do { \
if (Z_OBJ_APPLY_COUNT_P(zval_p) >= 3) { \
zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?"); \
} \
Z_OBJ_INC_APPLY_COUNT_P(zval_p); \
} while (0)
#define Z_OBJ_UNPROTECT_RECURSION(zval_p) \
Z_OBJ_DEC_APPLY_COUNT_P(zval_p)
/*
__X accessors explanation:
@ -1444,6 +1432,9 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
zobj1 = Z_OBJ_P(o1);
zobj2 = Z_OBJ_P(o2);
if (zobj1 == zobj2) {
return 0; /* the same object */
}
if (zobj1->ce != zobj2->ce) {
return 1; /* different classes */
}
@ -1456,40 +1447,45 @@ static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
p1 = zobj1->properties_table;
p2 = zobj2->properties_table;
end = p1 + zobj1->ce->default_properties_count;
Z_OBJ_PROTECT_RECURSION(o1);
Z_OBJ_PROTECT_RECURSION(o2);
/* use bitwise OR to make only one conditional jump */
if (UNEXPECTED(Z_IS_RECURSIVE_P(o1) | Z_IS_RECURSIVE_P(o2))) {
zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
}
Z_PROTECT_RECURSION_P(o1);
Z_PROTECT_RECURSION_P(o2);
do {
if (Z_TYPE_P(p1) != IS_UNDEF) {
if (Z_TYPE_P(p2) != IS_UNDEF) {
zval result;
if (compare_function(&result, p1, p2)==FAILURE) {
Z_OBJ_UNPROTECT_RECURSION(o1);
Z_OBJ_UNPROTECT_RECURSION(o2);
Z_UNPROTECT_RECURSION_P(o1);
Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
if (Z_LVAL(result) != 0) {
Z_OBJ_UNPROTECT_RECURSION(o1);
Z_OBJ_UNPROTECT_RECURSION(o2);
Z_UNPROTECT_RECURSION_P(o1);
Z_UNPROTECT_RECURSION_P(o2);
return Z_LVAL(result);
}
} else {
Z_OBJ_UNPROTECT_RECURSION(o1);
Z_OBJ_UNPROTECT_RECURSION(o2);
Z_UNPROTECT_RECURSION_P(o1);
Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
} else {
if (Z_TYPE_P(p2) != IS_UNDEF) {
Z_OBJ_UNPROTECT_RECURSION(o1);
Z_OBJ_UNPROTECT_RECURSION(o2);
Z_UNPROTECT_RECURSION_P(o1);
Z_UNPROTECT_RECURSION_P(o2);
return 1;
}
}
p1++;
p2++;
} while (p1 != end);
Z_OBJ_UNPROTECT_RECURSION(o1);
Z_OBJ_UNPROTECT_RECURSION(o2);
Z_UNPROTECT_RECURSION_P(o1);
Z_UNPROTECT_RECURSION_P(o2);
return 0;
} else {
if (!zobj1->properties) {

View File

@ -607,7 +607,7 @@ try_again:
/* fast copy */
if (!Z_OBJCE_P(op)->default_properties_count &&
obj_ht == Z_OBJ_P(op)->properties &&
!ZEND_HASH_GET_APPLY_COUNT(Z_OBJ_P(op)->properties) &&
!GC_IS_RECURSIVE(obj_ht) &&
EXPECTED(Z_OBJ_P(op)->handlers == &std_object_handlers)) {
arr = zend_proptable_to_symtable(obj_ht, 0);
} else {

View File

@ -69,16 +69,6 @@ ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pD
_zend_hash_init(TS_HASH(ht), nSize, pDestructor, persistent);
}
ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection)
{
#ifdef ZTS
ht->mx_reader = tsrm_mutex_alloc();
ht->mx_writer = tsrm_mutex_alloc();
ht->reader = 0;
#endif
_zend_hash_init_ex(TS_HASH(ht), nSize, pDestructor, persistent, bApplyProtection);
}
ZEND_API void zend_ts_hash_destroy(TsHashTable *ht)
{
begin_write(ht);

View File

@ -38,14 +38,13 @@ BEGIN_EXTERN_C()
/* startup/shutdown */
ZEND_API void _zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent);
ZEND_API void _zend_ts_hash_init_ex(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection);
ZEND_API void zend_ts_hash_destroy(TsHashTable *ht);
ZEND_API void zend_ts_hash_clean(TsHashTable *ht);
#define zend_ts_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \
_zend_ts_hash_init(ht, nSize, pDestructor, persistent)
#define zend_ts_hash_init_ex(ht, nSize, pHashFunction, pDestructor, persistent, bApplyProtection) \
_zend_ts_hash_init_ex(ht, nSize, pDestructor, persistent, bApplyProtection)
_zend_ts_hash_init(ht, nSize, pDestructor, persistent)
/* additions/updates/changes */

View File

@ -239,7 +239,7 @@ struct _zend_array {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar _unused,
zend_uchar nIteratorsCount,
zend_uchar consistency)
} v;
@ -455,7 +455,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
#define GC_INFO_SHIFT 16
#define GC_INFO_MASK 0xffff0000
/* zval.value->gc.u.v.flags */
/* zval.value->gc.u.v.flags (common flags) */
#define GC_PROTECTED (1<<0) /* used for array/object recursion detection */
#define GC_IMMUTABLE (1<<1) /* string/array can't be canged in place */
#define GC_COLLECTABLE (1<<7)
#define GC_ARRAY (IS_ARRAY | (GC_COLLECTABLE << GC_FLAGS_SHIFT))
@ -490,37 +492,38 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
/* string flags (zval.value->gc.u.flags) */
#define IS_STR_PERSISTENT (1<<0) /* allocated using malloc */
#define IS_STR_INTERNED (1<<1) /* interned string */
#define IS_STR_INTERNED GC_IMMUTABLE /* interned string */
#define IS_STR_PERMANENT (1<<2) /* relives request boundary */
/* array flags */
#define IS_ARRAY_IMMUTABLE (1<<1)
#define IS_ARRAY_PROTECTED GC_PROTECTED
#define IS_ARRAY_IMMUTABLE GC_IMMUTABLE
/* object flags (zval.value->gc.u.flags) */
#define IS_OBJ_APPLY_COUNT 0x07
#define IS_OBJ_PROTECTED GC_PROTECTED
#define IS_OBJ_DESTRUCTOR_CALLED (1<<3)
#define IS_OBJ_FREE_CALLED (1<<4)
#define IS_OBJ_USE_GUARDS (1<<5)
#define IS_OBJ_HAS_GUARDS (1<<6)
#define Z_OBJ_APPLY_COUNT(zval) \
(Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT)
/* Recursion protection macros must be used only for arrays and objects */
#define GC_IS_RECURSIVE(p) \
(GC_FLAGS(p) & GC_PROTECTED)
#define Z_OBJ_INC_APPLY_COUNT(zval) do { \
Z_GC_FLAGS(zval) = \
(Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) + 1); \
#define GC_PROTECT_RECURSION(p) do { \
GC_FLAGS(p) |= GC_PROTECTED; \
} while (0)
#define Z_OBJ_DEC_APPLY_COUNT(zval) do { \
Z_GC_FLAGS(zval) = \
(Z_GC_FLAGS(zval) & ~IS_OBJ_APPLY_COUNT) | \
((Z_GC_FLAGS(zval) & IS_OBJ_APPLY_COUNT) - 1); \
#define GC_UNPROTECT_RECURSION(p) do { \
GC_FLAGS(p) &= ~GC_PROTECTED; \
} while (0)
#define Z_OBJ_APPLY_COUNT_P(zv) Z_OBJ_APPLY_COUNT(*(zv))
#define Z_OBJ_INC_APPLY_COUNT_P(zv) Z_OBJ_INC_APPLY_COUNT(*(zv))
#define Z_OBJ_DEC_APPLY_COUNT_P(zv) Z_OBJ_DEC_APPLY_COUNT(*(zv))
#define Z_IS_RECURSIVE(zval) GC_IS_RECURSIVE(Z_COUNTED(zval))
#define Z_PROTECT_RECURSION(zval) GC_PROTECT_RECURSION(Z_COUNTED(zval))
#define Z_UNPROTECT_RECURSION(zval) GC_UNPROTECT_RECURSION(Z_COUNTED(zval))
#define Z_IS_RECURSIVE_P(zv) Z_IS_RECURSIVE(*(zv))
#define Z_PROTECT_RECURSION_P(zv) Z_PROTECT_RECURSION(*(zv))
#define Z_UNPROTECT_RECURSION_P(zv) Z_UNPROTECT_RECURSION(*(zv))
/* All data types < IS_STRING have their constructor/destructors skipped */
#define Z_CONSTANT(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_CONSTANT) != 0)

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -49,5 +49,16 @@ array(5) {
[3]=>
string(21) "日本語テキスト"
[4]=>
*RECURSION*
}
&array(5) {
[0]=>
string(21) "日本語テキスト"
[1]=>
string(21) "日本語テキスト"
[2]=>
string(21) "日本語テキスト"
[3]=>
string(21) "日本語テキスト"
[4]=>
*RECURSION*
}
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -36,10 +36,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"]=>

View File

@ -35,5 +35,5 @@ int(4)
-- $mode = 1: --
Warning: count(): recursion detected in %s on line %d
int(12)
int(4)
Done

View File

@ -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

View File

@ -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);
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 {
if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
Z_ARRVAL_P(data)->u.v.nApplyCount++;
}
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();
}

View File

@ -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);
}
}
}
}

View File

@ -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;

View File

@ -687,10 +687,12 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
break;
case IS_ARRAY:
myht = Z_ARRVAL_P(zv);
if (ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
phpdbg_xml("<recursion />");
--myht->u.v.nApplyCount;
break;
if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
if (GC_IS_RECURSIVE(myht)) {
phpdbg_xml("<recursion />");
break;
}
GC_PROTECT_RECURSION(myht);
}
phpdbg_xml("<array refstatus=\"%s\" num=\"%d\">", COMMON, zend_hash_num_elements(myht));
element_dump_func = phpdbg_xml_array_element_dump;
@ -698,9 +700,8 @@ PHPDBG_API void phpdbg_xml_var_dump(zval *zv) {
goto head_done;
case IS_OBJECT:
myht = Z_OBJDEBUG_P(zv, is_temp);
if (myht && ++myht->u.v.nApplyCount > 1) {
if (myht && GC_IS_RECURSIVE(myht)) {
phpdbg_xml("<recursion />");
--myht->u.v.nApplyCount;
break;
}
@ -715,7 +716,7 @@ head_done:
element_dump_func(val, key, num);
} ZEND_HASH_FOREACH_END();
zend_hash_apply_with_arguments(myht, (apply_func_args_t) element_dump_func, 0);
--myht->u.v.nApplyCount;
GC_UNPROTECT_RECURSION(myht);
if (is_temp) {
zend_hash_destroy(myht);
efree(myht);