diff --git a/Zend/tests/bug70644.phpt b/Zend/tests/bug70644.phpt new file mode 100644 index 0000000000000..bf9db60a3c6ff --- /dev/null +++ b/Zend/tests/bug70644.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #70644: trivial hash complexity DoS attack (plain array) +--FILE-- + +--EXPECTF-- +Fatal error: Too many collisions in hashtable in %s on line %d diff --git a/Zend/tests/bug70644_2.phpt b/Zend/tests/bug70644_2.phpt new file mode 100644 index 0000000000000..12d60db9a24f7 --- /dev/null +++ b/Zend/tests/bug70644_2.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #70644: trivial hash complexity DoS attack (json_decode) +--FILE-- + +--EXPECTF-- +Fatal error: Too many collisions in hashtable in %s on line %d diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 044b49e7606eb..aeadfe288b505 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1569,6 +1569,10 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { hval = Z_LVAL_P(dim); num_index: + if (type == BP_VAR_W) { + return zend_hash_index_add_or_return(ht, hval, &EG(uninitialized_zval)); + } + retval = zend_hash_index_find(ht, hval); if (retval == NULL) { switch (type) { @@ -1583,9 +1587,7 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval)); break; - case BP_VAR_W: - retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval)); - break; + EMPTY_SWITCH_DEFAULT_CASE() } } } else if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) { @@ -1596,6 +1598,17 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht } } str_index: + if (type == BP_VAR_W) { + retval = zend_hash_add_or_return(ht, offset_key, &EG(uninitialized_zval)); + if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) { + retval = Z_INDIRECT_P(retval); + if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { + ZVAL_NULL(retval); + } + } + return retval; + } + retval = zend_hash_find(ht, offset_key); if (retval) { /* support for $GLOBALS[...] */ @@ -1612,10 +1625,9 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht break; case BP_VAR_RW: zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); - /* break missing intentionally */ - case BP_VAR_W: ZVAL_NULL(retval); break; + EMPTY_SWITCH_DEFAULT_CASE() } } } @@ -1632,9 +1644,7 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); retval = zend_hash_update(ht, offset_key, &EG(uninitialized_zval)); break; - case BP_VAR_W: - retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval)); - break; + EMPTY_SWITCH_DEFAULT_CASE() } } } else { diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 283ceb8c18dd3..5abc09217f2ae 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -459,12 +459,25 @@ ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosit } } -static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) +/* To protect against HashDos attacks, we count collisions during the "find + * existing bucket with this key" phase of insertion operations. The check + * against MAX_COLLISIONS is only performed if *no* matching bucket is found, + * as that corresponds to insert operations (as opposed to updates). + * + * An important caveat of the implementation is that collisions will *not* + * be counted for add_new operations. find+add_new operations should be replaced + * with add_or_return operations. + */ +#define HT_MAX_COLLISIONS 1000 + +static zend_always_inline Bucket *zend_hash_find_bucket( + const HashTable *ht, zend_string *key, zend_bool for_insert) { zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p, *arData; + uint32_t num_collisions = 0; h = zend_string_hash_val(key); arData = ht->arData; @@ -481,15 +494,21 @@ static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zen return p; } idx = Z_NEXT(p->val); + num_collisions++; + } + if (for_insert && UNEXPECTED(num_collisions > HT_MAX_COLLISIONS)) { + zend_error_noreturn(E_ERROR, "Too many collisions in hashtable"); } return NULL; } -static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, const char *str, size_t len, zend_ulong h) +static zend_always_inline Bucket *zend_hash_str_find_bucket( + const HashTable *ht, const char *str, size_t len, zend_ulong h, zend_bool for_insert) { uint32_t nIndex; uint32_t idx; Bucket *p, *arData; + uint32_t num_collisions = 0; arData = ht->arData; nIndex = h | ht->nTableMask; @@ -504,15 +523,21 @@ static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, return p; } idx = Z_NEXT(p->val); + num_collisions++; + } + if (for_insert && UNEXPECTED(num_collisions > HT_MAX_COLLISIONS)) { + zend_error_noreturn(E_ERROR, "Too many collisions in hashtable"); } return NULL; } -static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h) +static zend_always_inline Bucket *zend_hash_index_find_bucket( + const HashTable *ht, zend_ulong h, zend_bool for_insert) { uint32_t nIndex; uint32_t idx; Bucket *p, *arData; + uint32_t num_collisions = 0; arData = ht->arData; nIndex = h | ht->nTableMask; @@ -524,6 +549,10 @@ static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *h return p; } idx = Z_NEXT(p->val); + num_collisions++; + } + if (for_insert && UNEXPECTED(num_collisions > HT_MAX_COLLISIONS)) { + zend_error_noreturn(E_ERROR, "Too many collisions in hashtable"); } return NULL; } @@ -544,7 +573,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s } else if (ht->u.flags & HASH_FLAG_PACKED) { zend_hash_packed_to_hash(ht); } else if ((flag & HASH_ADD_NEW) == 0) { - p = zend_hash_find_bucket(ht, key); + p = zend_hash_find_bucket(ht, key, 1); if (p) { zval *data; @@ -552,6 +581,9 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s if (flag & HASH_ADD) { return NULL; } + if (flag & HASH_ADD_OR_RETURN) { + return &p->val; + } ZEND_ASSERT(&p->val != pData); data = &p->val; if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) { @@ -619,6 +651,11 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_add_new(HashTable *ht, zend_string *key, return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC); } +ZEND_API zval* ZEND_FASTCALL _zend_hash_add_or_return(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC) +{ + return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_OR_RETURN ZEND_FILE_LINE_RELAY_CC); +} + ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_or_update(HashTable *ht, const char *str, size_t len, zval *pData, uint32_t flag ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); @@ -706,6 +743,9 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, if (flag & HASH_ADD) { return NULL; } + if (flag & HASH_ADD_OR_RETURN) { + return &p->val; + } if (ht->pDestructor) { ht->pDestructor(&p->val); } @@ -761,11 +801,14 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, convert_to_hash: zend_hash_packed_to_hash(ht); } else if ((flag & HASH_ADD_NEW) == 0) { - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, h, 1); if (p) { if (flag & HASH_ADD) { return NULL; } + if (flag & HASH_ADD_OR_RETURN) { + return &p->val; + } ZEND_ASSERT(&p->val != pData); HANDLE_BLOCK_INTERRUPTIONS(); if (ht->pDestructor) { @@ -820,6 +863,11 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add_new(HashTable *ht, zend_ulong return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD | HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC); } +ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add_or_return(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC) +{ + return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD_OR_RETURN ZEND_FILE_LINE_RELAY_CC); +} + ZEND_API zval* ZEND_FASTCALL _zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC) { return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_UPDATE ZEND_FILE_LINE_RELAY_CC); @@ -1923,7 +1971,7 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *ke IS_CONSISTENT(ht); - p = zend_hash_find_bucket(ht, key); + p = zend_hash_find_bucket(ht, key, 0); return p ? &p->val : NULL; } @@ -1935,7 +1983,7 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char IS_CONSISTENT(ht); h = zend_inline_hash_func(str, len); - p = zend_hash_str_find_bucket(ht, str, len, h); + p = zend_hash_str_find_bucket(ht, str, len, h, 0); return p ? &p->val : NULL; } @@ -1945,7 +1993,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_exists(const HashTable *ht, zend_stri IS_CONSISTENT(ht); - p = zend_hash_find_bucket(ht, key); + p = zend_hash_find_bucket(ht, key, 0); return p ? 1 : 0; } @@ -1957,7 +2005,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_str_exists(const HashTable *ht, const IS_CONSISTENT(ht); h = zend_inline_hash_func(str, len); - p = zend_hash_str_find_bucket(ht, str, len, h); + p = zend_hash_str_find_bucket(ht, str, len, h, 0); return p ? 1 : 0; } @@ -1977,11 +2025,10 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulon return NULL; } - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, h, 0); return p ? &p->val : NULL; } - ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h) { Bucket *p; @@ -1997,7 +2044,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zen return 0; } - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, h, 0); return p ? 1 : 0; } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 9fe99ac919a2f..e62078ebbe984 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -34,6 +34,7 @@ #define HASH_UPDATE_INDIRECT (1<<2) #define HASH_ADD_NEW (1<<3) #define HASH_ADD_NEXT (1<<4) +#define HASH_ADD_OR_RETURN (1<<5) #define HASH_FLAG_PERSISTENT (1<<0) #define HASH_FLAG_APPLY_PROTECTION (1<<1) @@ -72,6 +73,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_update(HashTable *ht, zend_string *key,z ZEND_API zval* ZEND_FASTCALL _zend_hash_update_ind(HashTable *ht, zend_string *key,zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_add(HashTable *ht, zend_string *key,zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_add_new(HashTable *ht, zend_string *key,zval *pData ZEND_FILE_LINE_DC); +ZEND_API zval* ZEND_FASTCALL _zend_hash_add_or_return(HashTable *ht, zend_string *key, zval *pData ZEND_FILE_LINE_DC); #define zend_hash_update(ht, key, pData) \ _zend_hash_update(ht, key, pData ZEND_FILE_LINE_CC) @@ -81,6 +83,8 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_add_new(HashTable *ht, zend_string *key, _zend_hash_add(ht, key, pData ZEND_FILE_LINE_CC) #define zend_hash_add_new(ht, key, pData) \ _zend_hash_add_new(ht, key, pData ZEND_FILE_LINE_CC) +#define zend_hash_add_or_return(ht, key, pData) \ + _zend_hash_add_or_return(ht, key, pData ZEND_FILE_LINE_CC) ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_or_update(HashTable *ht, const char *key, size_t len, zval *pData, uint32_t flag ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_str_update(HashTable *ht, const char *key, size_t len, zval *pData ZEND_FILE_LINE_DC); @@ -100,6 +104,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_new(HashTable *ht, const char *k ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add_or_update(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC); +ZEND_API zval* ZEND_FASTCALL _zend_hash_index_add_or_return(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_next_index_insert(HashTable *ht, zval *pData ZEND_FILE_LINE_DC); ZEND_API zval* ZEND_FASTCALL _zend_hash_next_index_insert_new(HashTable *ht, zval *pData ZEND_FILE_LINE_DC); @@ -108,6 +113,8 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_next_index_insert_new(HashTable *ht, zva _zend_hash_index_add(ht, h, pData ZEND_FILE_LINE_CC) #define zend_hash_index_add_new(ht, h, pData) \ _zend_hash_index_add_new(ht, h, pData ZEND_FILE_LINE_CC) +#define zend_hash_index_add_or_return(ht, h, pData) \ + _zend_hash_index_add_or_return(ht, h, pData ZEND_FILE_LINE_CC) #define zend_hash_index_update(ht, h, pData) \ _zend_hash_index_update(ht, h, pData ZEND_FILE_LINE_CC) #define zend_hash_next_index_insert(ht, pData) \ @@ -150,7 +157,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *key, ZEND_API int ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h); ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p); -/* Data retreival */ +/* Data retrieval */ ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key); ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *key, size_t len); ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a6537800fd289..ebb61115fd90c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1495,29 +1495,17 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int ty } target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK); - retval = zend_hash_find(target_symbol_table, name); - if (retval == NULL) { - switch (type) { - case BP_VAR_R: - case BP_VAR_UNSET: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_IS: - retval = &EG(uninitialized_zval); - break; - case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); - break; - case BP_VAR_W: - retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval)); - break; - EMPTY_SWITCH_DEFAULT_CASE() + if (type == BP_VAR_W) { + retval = zend_hash_add_or_return(target_symbol_table, name, &EG(uninitialized_zval)); + if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + ZVAL_NULL(retval); + } } - /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ - } else if (Z_TYPE_P(retval) == IS_INDIRECT) { - retval = Z_INDIRECT_P(retval); - if (Z_TYPE_P(retval) == IS_UNDEF) { + } else { + retval = zend_hash_find(target_symbol_table, name); + if (retval == NULL) { switch (type) { case BP_VAR_R: case BP_VAR_UNSET: @@ -1528,12 +1516,29 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int ty break; case BP_VAR_RW: zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_W: - ZVAL_NULL(retval); + retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); break; EMPTY_SWITCH_DEFAULT_CASE() } + /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ + } else if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + switch (type) { + case BP_VAR_R: + case BP_VAR_UNSET: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + ZVAL_NULL(retval); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 975143a50a180..65c6e1e010ed7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7167,29 +7167,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ } target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK); - retval = zend_hash_find(target_symbol_table, name); - if (retval == NULL) { - switch (type) { - case BP_VAR_R: - case BP_VAR_UNSET: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_IS: - retval = &EG(uninitialized_zval); - break; - case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); - break; - case BP_VAR_W: - retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval)); - break; - EMPTY_SWITCH_DEFAULT_CASE() + if (type == BP_VAR_W) { + retval = zend_hash_add_or_return(target_symbol_table, name, &EG(uninitialized_zval)); + if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + ZVAL_NULL(retval); + } } - /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ - } else if (Z_TYPE_P(retval) == IS_INDIRECT) { - retval = Z_INDIRECT_P(retval); - if (Z_TYPE_P(retval) == IS_UNDEF) { + } else { + retval = zend_hash_find(target_symbol_table, name); + if (retval == NULL) { switch (type) { case BP_VAR_R: case BP_VAR_UNSET: @@ -7200,12 +7188,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ break; case BP_VAR_RW: zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_W: - ZVAL_NULL(retval); + retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); break; EMPTY_SWITCH_DEFAULT_CASE() } + /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ + } else if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + switch (type) { + case BP_VAR_R: + case BP_VAR_UNSET: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + ZVAL_NULL(retval); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } } } @@ -34518,29 +34523,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ } target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK); - retval = zend_hash_find(target_symbol_table, name); - if (retval == NULL) { - switch (type) { - case BP_VAR_R: - case BP_VAR_UNSET: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_IS: - retval = &EG(uninitialized_zval); - break; - case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); - break; - case BP_VAR_W: - retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval)); - break; - EMPTY_SWITCH_DEFAULT_CASE() + if (type == BP_VAR_W) { + retval = zend_hash_add_or_return(target_symbol_table, name, &EG(uninitialized_zval)); + if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + ZVAL_NULL(retval); + } } - /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ - } else if (Z_TYPE_P(retval) == IS_INDIRECT) { - retval = Z_INDIRECT_P(retval); - if (Z_TYPE_P(retval) == IS_UNDEF) { + } else { + retval = zend_hash_find(target_symbol_table, name); + if (retval == NULL) { switch (type) { case BP_VAR_R: case BP_VAR_UNSET: @@ -34551,12 +34544,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ break; case BP_VAR_RW: zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_W: - ZVAL_NULL(retval); + retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); break; EMPTY_SWITCH_DEFAULT_CASE() } + /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ + } else if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + switch (type) { + case BP_VAR_R: + case BP_VAR_UNSET: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + ZVAL_NULL(retval); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } } } @@ -43119,29 +43129,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ } target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK); - retval = zend_hash_find(target_symbol_table, name); - if (retval == NULL) { - switch (type) { - case BP_VAR_R: - case BP_VAR_UNSET: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_IS: - retval = &EG(uninitialized_zval); - break; - case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); - break; - case BP_VAR_W: - retval = zend_hash_add_new(target_symbol_table, name, &EG(uninitialized_zval)); - break; - EMPTY_SWITCH_DEFAULT_CASE() + if (type == BP_VAR_W) { + retval = zend_hash_add_or_return(target_symbol_table, name, &EG(uninitialized_zval)); + if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + ZVAL_NULL(retval); + } } - /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ - } else if (Z_TYPE_P(retval) == IS_INDIRECT) { - retval = Z_INDIRECT_P(retval); - if (Z_TYPE_P(retval) == IS_UNDEF) { + } else { + retval = zend_hash_find(target_symbol_table, name); + if (retval == NULL) { switch (type) { case BP_VAR_R: case BP_VAR_UNSET: @@ -43152,12 +43150,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ break; case BP_VAR_RW: zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); - /* break missing intentionally */ - case BP_VAR_W: - ZVAL_NULL(retval); + retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); break; EMPTY_SWITCH_DEFAULT_CASE() } + /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ + } else if (Z_TYPE_P(retval) == IS_INDIRECT) { + retval = Z_INDIRECT_P(retval); + if (Z_TYPE_P(retval) == IS_UNDEF) { + switch (type) { + case BP_VAR_R: + case BP_VAR_UNSET: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined variable: %s", ZSTR_VAL(name)); + ZVAL_NULL(retval); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } } }