diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-02-04 14:19:07 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-07-07 12:13:58 +0200 |
commit | 220880ad2d54d10173a250637478da213b1ae8e2 (patch) | |
tree | 8062ba643a43dfc0b5dbab4564cf1f552041042d /Zend/zend_execute.c | |
parent | 48a247178ef54bc2452cc4409b17e1d5d7321f0d (diff) | |
download | php-git-220880ad2d54d10173a250637478da213b1ae8e2.tar.gz |
Fixed bug #78598
When performing an RW modification of an array offset, the undefined
offset warning may call an error handler / OB callback, which may
destroy the array we're supposed to change. Detect this by temporarily
incrementing the reference count. If we find that the array has been
modified/destroyed in the meantime, we do nothing -- the execution
model here would be that the modification has happened on the destroyed
version of the array.
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 56 |
1 files changed, 49 insertions, 7 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 89e6178019..7b27c5a3a5 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1958,6 +1958,44 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_index(const zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset)); } +static zend_never_inline ZEND_COLD int ZEND_FASTCALL zend_undefined_offset_write( + HashTable *ht, zend_long lval) +{ + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + zend_undefined_offset(lval); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + return FAILURE; + } + if (EG(exception)) { + return FAILURE; + } + return SUCCESS; +} + +static zend_never_inline ZEND_COLD int ZEND_FASTCALL zend_undefined_index_write( + HashTable *ht, zend_string *offset) +{ + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + zend_undefined_index(offset); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + return FAILURE; + } + if (EG(exception)) { + return FAILURE; + } + return SUCCESS; +} + static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method) { zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method)); @@ -2079,9 +2117,10 @@ num_undef: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_undefined_offset(hval); - retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval)); - break; + if (UNEXPECTED(zend_undefined_offset_write(ht, hval) == FAILURE)) { + return NULL; + } + /* break missing intentionally */ case BP_VAR_W: retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval)); break; @@ -2109,7 +2148,9 @@ str_index: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_undefined_index(offset_key); + if (UNEXPECTED(zend_undefined_index_write(ht, offset_key))) { + return NULL; + } /* break missing intentionally */ case BP_VAR_W: ZVAL_NULL(retval); @@ -2127,9 +2168,10 @@ str_index: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_undefined_index(offset_key); - retval = zend_hash_update(ht, offset_key, &EG(uninitialized_zval)); - break; + if (UNEXPECTED(zend_undefined_index_write(ht, offset_key) == FAILURE)) { + return NULL; + } + /* break missing intentionally */ case BP_VAR_W: retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval)); break; |