mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
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.
This commit is contained in:
parent
48a247178e
commit
220880ad2d
2
NEWS
2
NEWS
@ -19,6 +19,8 @@ PHP NEWS
|
||||
offset by reference). (Nikita)
|
||||
. Fixed bug #79792 (HT iterators not removed if empty array is destroyed).
|
||||
(Nikita)
|
||||
. Fixed bug #78598 (Changing array during undef index RW error segfaults).
|
||||
(Nikita)
|
||||
|
||||
- Fileinfo:
|
||||
. Fixed bug #79756 (finfo_file crash (FILEINFO_MIME)). (cmb)
|
||||
|
@ -14,5 +14,5 @@ var_dump($a);
|
||||
--EXPECT--
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
int(2)
|
||||
}
|
||||
|
31
Zend/tests/bug78598.phpt
Normal file
31
Zend/tests/bug78598.phpt
Normal file
@ -0,0 +1,31 @@
|
||||
--TEST--
|
||||
Bug #78598: Changing array during undef index RW error segfaults
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$my_var = null;
|
||||
set_error_handler(function() use(&$my_var) {
|
||||
$my_var = 0;
|
||||
});
|
||||
|
||||
$my_var[0] .= "xyz";
|
||||
var_dump($my_var);
|
||||
|
||||
$my_var = null;
|
||||
$my_var[0][0][0] .= "xyz";
|
||||
var_dump($my_var);
|
||||
|
||||
$my_var = null;
|
||||
$my_var["foo"] .= "xyz";
|
||||
var_dump($my_var);
|
||||
|
||||
$my_var = null;
|
||||
$my_var["foo"]["bar"]["baz"] .= "xyz";
|
||||
var_dump($my_var);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
int(0)
|
||||
int(0)
|
||||
int(0)
|
||||
int(0)
|
46
Zend/tests/undef_index_to_exception.phpt
Normal file
46
Zend/tests/undef_index_to_exception.phpt
Normal file
@ -0,0 +1,46 @@
|
||||
--TEST--
|
||||
Converting undefined index/offset notice to exception
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(function($_, $msg) {
|
||||
throw new Exception($msg);
|
||||
});
|
||||
|
||||
$test = [];
|
||||
try {
|
||||
$test[0] .= "xyz";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump($test);
|
||||
|
||||
try {
|
||||
$test["key"] .= "xyz";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump($test);
|
||||
|
||||
unset($test);
|
||||
try {
|
||||
$GLOBALS["test"] .= "xyz";
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
var_dump($test);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Undefined offset: 0
|
||||
array(0) {
|
||||
}
|
||||
Undefined index: key
|
||||
array(0) {
|
||||
}
|
||||
Undefined index: test
|
||||
Undefined variable: test
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user