summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-07-07 12:17:11 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-07-07 12:28:35 +0200
commit2c73bf7e3fc470e8e50cc7283844c9b1d7c2a9d2 (patch)
tree8e1f2746f4ee9fd761804f3a8bd73085eb0774f3
parentecf368b8f2b9d578f3f9bb23f48d5920adbdccbb (diff)
parent220880ad2d54d10173a250637478da213b1ae8e2 (diff)
downloadphp-git-2c73bf7e3fc470e8e50cc7283844c9b1d7c2a9d2.tar.gz
Merge branch 'PHP-7.4'
* PHP-7.4: Fixed bug #78598
-rw-r--r--Zend/tests/bug70662.phpt2
-rw-r--r--Zend/tests/bug78598.phpt31
-rw-r--r--Zend/tests/undef_index_to_exception.phpt46
-rw-r--r--Zend/zend_execute.c60
4 files changed, 130 insertions, 9 deletions
diff --git a/Zend/tests/bug70662.phpt b/Zend/tests/bug70662.phpt
index 2bda8141ba..ab540c9d16 100644
--- a/Zend/tests/bug70662.phpt
+++ b/Zend/tests/bug70662.phpt
@@ -14,5 +14,5 @@ var_dump($a);
--EXPECT--
array(1) {
["b"]=>
- int(1)
+ int(2)
}
diff --git a/Zend/tests/bug78598.phpt b/Zend/tests/bug78598.phpt
new file mode 100644
index 0000000000..7e3559f27c
--- /dev/null
+++ b/Zend/tests/bug78598.phpt
@@ -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)
diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt
new file mode 100644
index 0000000000..919b0d47a2
--- /dev/null
+++ b/Zend/tests/undef_index_to_exception.phpt
@@ -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
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index fcd2a437e6..f49b005a14 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1909,6 +1909,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));
@@ -2028,9 +2066,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;
@@ -2058,7 +2097,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);
@@ -2076,9 +2117,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;
@@ -2143,7 +2185,9 @@ fetch_from_array:
} else {
retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type EXECUTE_DATA_CC);
if (UNEXPECTED(!retval)) {
- ZVAL_UNDEF(result);
+ /* This may fail without throwing if the array was modified while throwing an
+ * undefined index error. */
+ ZVAL_NULL(result);
return;
}
}