Skip to content

Commit 2c73bf7

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fixed bug #78598
2 parents ecf368b + 220880a commit 2c73bf7

File tree

4 files changed

+130
-9
lines changed

4 files changed

+130
-9
lines changed

Zend/tests/bug70662.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ var_dump($a);
1414
--EXPECT--
1515
array(1) {
1616
["b"]=>
17-
int(1)
17+
int(2)
1818
}

Zend/tests/bug78598.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Bug #78598: Changing array during undef index RW error segfaults
3+
--FILE--
4+
<?php
5+
6+
$my_var = null;
7+
set_error_handler(function() use(&$my_var) {
8+
$my_var = 0;
9+
});
10+
11+
$my_var[0] .= "xyz";
12+
var_dump($my_var);
13+
14+
$my_var = null;
15+
$my_var[0][0][0] .= "xyz";
16+
var_dump($my_var);
17+
18+
$my_var = null;
19+
$my_var["foo"] .= "xyz";
20+
var_dump($my_var);
21+
22+
$my_var = null;
23+
$my_var["foo"]["bar"]["baz"] .= "xyz";
24+
var_dump($my_var);
25+
26+
?>
27+
--EXPECT--
28+
int(0)
29+
int(0)
30+
int(0)
31+
int(0)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
Converting undefined index/offset notice to exception
3+
--FILE--
4+
<?php
5+
6+
set_error_handler(function($_, $msg) {
7+
throw new Exception($msg);
8+
});
9+
10+
$test = [];
11+
try {
12+
$test[0] .= "xyz";
13+
} catch (Exception $e) {
14+
echo $e->getMessage(), "\n";
15+
}
16+
var_dump($test);
17+
18+
try {
19+
$test["key"] .= "xyz";
20+
} catch (Exception $e) {
21+
echo $e->getMessage(), "\n";
22+
}
23+
var_dump($test);
24+
25+
unset($test);
26+
try {
27+
$GLOBALS["test"] .= "xyz";
28+
} catch (Exception $e) {
29+
echo $e->getMessage(), "\n";
30+
}
31+
try {
32+
var_dump($test);
33+
} catch (Exception $e) {
34+
echo $e->getMessage(), "\n";
35+
}
36+
37+
?>
38+
--EXPECT--
39+
Undefined offset: 0
40+
array(0) {
41+
}
42+
Undefined index: key
43+
array(0) {
44+
}
45+
Undefined index: test
46+
Undefined variable $test

Zend/zend_execute.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,44 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_index(const
19091909
zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset));
19101910
}
19111911

1912+
static zend_never_inline ZEND_COLD int ZEND_FASTCALL zend_undefined_offset_write(
1913+
HashTable *ht, zend_long lval)
1914+
{
1915+
/* The array may be destroyed while throwing the notice.
1916+
* Temporarily increase the refcount to detect this situation. */
1917+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
1918+
GC_ADDREF(ht);
1919+
}
1920+
zend_undefined_offset(lval);
1921+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
1922+
zend_array_destroy(ht);
1923+
return FAILURE;
1924+
}
1925+
if (EG(exception)) {
1926+
return FAILURE;
1927+
}
1928+
return SUCCESS;
1929+
}
1930+
1931+
static zend_never_inline ZEND_COLD int ZEND_FASTCALL zend_undefined_index_write(
1932+
HashTable *ht, zend_string *offset)
1933+
{
1934+
/* The array may be destroyed while throwing the notice.
1935+
* Temporarily increase the refcount to detect this situation. */
1936+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
1937+
GC_ADDREF(ht);
1938+
}
1939+
zend_undefined_index(offset);
1940+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
1941+
zend_array_destroy(ht);
1942+
return FAILURE;
1943+
}
1944+
if (EG(exception)) {
1945+
return FAILURE;
1946+
}
1947+
return SUCCESS;
1948+
}
1949+
19121950
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
19131951
{
19141952
zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method));
@@ -2028,9 +2066,10 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
20282066
retval = &EG(uninitialized_zval);
20292067
break;
20302068
case BP_VAR_RW:
2031-
zend_undefined_offset(hval);
2032-
retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval));
2033-
break;
2069+
if (UNEXPECTED(zend_undefined_offset_write(ht, hval) == FAILURE)) {
2070+
return NULL;
2071+
}
2072+
/* break missing intentionally */
20342073
case BP_VAR_W:
20352074
retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
20362075
break;
@@ -2058,7 +2097,9 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
20582097
retval = &EG(uninitialized_zval);
20592098
break;
20602099
case BP_VAR_RW:
2061-
zend_undefined_index(offset_key);
2100+
if (UNEXPECTED(zend_undefined_index_write(ht, offset_key))) {
2101+
return NULL;
2102+
}
20622103
/* break missing intentionally */
20632104
case BP_VAR_W:
20642105
ZVAL_NULL(retval);
@@ -2076,9 +2117,10 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
20762117
retval = &EG(uninitialized_zval);
20772118
break;
20782119
case BP_VAR_RW:
2079-
zend_undefined_index(offset_key);
2080-
retval = zend_hash_update(ht, offset_key, &EG(uninitialized_zval));
2081-
break;
2120+
if (UNEXPECTED(zend_undefined_index_write(ht, offset_key) == FAILURE)) {
2121+
return NULL;
2122+
}
2123+
/* break missing intentionally */
20822124
case BP_VAR_W:
20832125
retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval));
20842126
break;
@@ -2143,7 +2185,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
21432185
} else {
21442186
retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type EXECUTE_DATA_CC);
21452187
if (UNEXPECTED(!retval)) {
2146-
ZVAL_UNDEF(result);
2188+
/* This may fail without throwing if the array was modified while throwing an
2189+
* undefined index error. */
2190+
ZVAL_NULL(result);
21472191
return;
21482192
}
21492193
}

0 commit comments

Comments
 (0)