Skip to content

Commit 107997e

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Prevent array modification if it's captured by user error handler during index conversion
2 parents 233b659 + 7184783 commit 107997e

File tree

3 files changed

+126
-15
lines changed

3 files changed

+126
-15
lines changed

Zend/tests/array_offset_002.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Capturing array in user error handler during index conversion
3+
--FILE--
4+
<?php
5+
set_error_handler(function($code, $msg) {
6+
echo "Err: $msg\n";
7+
$GLOBALS[''] = $GLOBALS['y'];
8+
});
9+
function x(&$s){
10+
$s[100000000000000000000] = 1;
11+
}
12+
x($y);
13+
var_dump($y);
14+
?>
15+
--EXPECT--
16+
Err: Implicit conversion from float 1.0E+20 to int loses precision
17+
array(0) {
18+
}

Zend/zend_execute.c

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,8 +2073,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht
20732073
GC_ADDREF(ht);
20742074
}
20752075
zend_undefined_offset(lval);
2076-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
2077-
zend_array_destroy(ht);
2076+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2077+
if (!GC_REFCOUNT(ht)) {
2078+
zend_array_destroy(ht);
2079+
}
20782080
return NULL;
20792081
}
20802082
if (EG(exception)) {
@@ -2095,8 +2097,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht,
20952097
/* Key may be released while throwing the undefined index warning. */
20962098
zend_string_addref(offset);
20972099
zend_undefined_index(offset);
2098-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
2099-
zend_array_destroy(ht);
2100+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2101+
if (!GC_REFCOUNT(ht)) {
2102+
zend_array_destroy(ht);
2103+
}
21002104
retval = NULL;
21012105
} else if (EG(exception)) {
21022106
retval = NULL;
@@ -2245,6 +2249,80 @@ static zend_never_inline zend_uchar slow_index_convert(HashTable *ht, const zval
22452249
}
22462250
}
22472251

2252+
static zend_never_inline zend_uchar slow_index_convert_w(HashTable *ht, const zval *dim, zend_value *value EXECUTE_DATA_DC)
2253+
{
2254+
switch (Z_TYPE_P(dim)) {
2255+
case IS_UNDEF: {
2256+
/* The array may be destroyed while throwing the notice.
2257+
* Temporarily increase the refcount to detect this situation. */
2258+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2259+
GC_ADDREF(ht);
2260+
}
2261+
ZVAL_UNDEFINED_OP2();
2262+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2263+
if (!GC_REFCOUNT(ht)) {
2264+
zend_array_destroy(ht);
2265+
}
2266+
return IS_NULL;
2267+
}
2268+
if (EG(exception)) {
2269+
return IS_NULL;
2270+
}
2271+
ZEND_FALLTHROUGH;
2272+
}
2273+
case IS_NULL:
2274+
value->str = ZSTR_EMPTY_ALLOC();
2275+
return IS_STRING;
2276+
case IS_DOUBLE:
2277+
value->lval = zend_dval_to_lval(Z_DVAL_P(dim));
2278+
if (!zend_is_long_compatible(Z_DVAL_P(dim), value->lval)) {
2279+
/* The array may be destroyed while throwing the notice.
2280+
* Temporarily increase the refcount to detect this situation. */
2281+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2282+
GC_ADDREF(ht);
2283+
}
2284+
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
2285+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2286+
if (!GC_REFCOUNT(ht)) {
2287+
zend_array_destroy(ht);
2288+
}
2289+
return IS_NULL;
2290+
}
2291+
if (EG(exception)) {
2292+
return IS_NULL;
2293+
}
2294+
}
2295+
return IS_LONG;
2296+
case IS_RESOURCE:
2297+
/* The array may be destroyed while throwing the notice.
2298+
* Temporarily increase the refcount to detect this situation. */
2299+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2300+
GC_ADDREF(ht);
2301+
}
2302+
zend_use_resource_as_offset(dim);
2303+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2304+
if (!GC_REFCOUNT(ht)) {
2305+
zend_array_destroy(ht);
2306+
}
2307+
return IS_NULL;
2308+
}
2309+
if (EG(exception)) {
2310+
return IS_NULL;
2311+
}
2312+
value->lval = Z_RES_HANDLE_P(dim);
2313+
return IS_LONG;
2314+
case IS_FALSE:
2315+
value->lval = 0;
2316+
return IS_LONG;
2317+
case IS_TRUE:
2318+
value->lval = 1;
2319+
return IS_LONG;
2320+
default:
2321+
zend_illegal_offset();
2322+
return IS_NULL;
2323+
}
2324+
}
2325+
22482326
static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type EXECUTE_DATA_DC)
22492327
{
22502328
zval *retval = NULL;
@@ -2306,8 +2384,13 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
23062384
goto try_again;
23072385
} else {
23082386
zend_value val;
2309-
zend_uchar t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
2387+
zend_uchar t;
23102388

2389+
if (type != BP_VAR_W && type != BP_VAR_RW) {
2390+
t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
2391+
} else {
2392+
t = slow_index_convert_w(ht, dim, &val EXECUTE_DATA_CC);
2393+
}
23112394
if (t == IS_STRING) {
23122395
offset_key = val.str;
23132396
goto str_index;

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,10 @@ static int ZEND_FASTCALL zend_jit_undefined_op_helper_write(HashTable *ht, uint3
365365
GC_ADDREF(ht);
366366
}
367367
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
368-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
369-
zend_array_destroy(ht);
368+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
369+
if (!GC_REFCOUNT(ht)) {
370+
zend_array_destroy(ht);
371+
}
370372
return 0;
371373
}
372374
return EG(exception) == NULL;
@@ -808,8 +810,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di
808810
execute_data = EG(current_execute_data);
809811
opline = EX(opline);
810812
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
811-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
812-
zend_array_destroy(ht);
813+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
814+
if (GC_REFCOUNT(ht)) {
815+
zend_array_destroy(ht);
816+
}
813817
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
814818
if (EG(exception)) {
815819
ZVAL_UNDEF(EX_VAR(opline->result.var));
@@ -836,8 +840,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di
836840
execute_data = EG(current_execute_data);
837841
opline = EX(opline);
838842
zend_use_resource_as_offset(dim);
839-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
840-
zend_array_destroy(ht);
843+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
844+
if (GC_REFCOUNT(ht)) {
845+
zend_array_destroy(ht);
846+
}
841847
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
842848
if (EG(exception)) {
843849
ZVAL_UNDEF(EX_VAR(opline->result.var));
@@ -933,8 +939,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim
933939
execute_data = EG(current_execute_data);
934940
opline = EX(opline);
935941
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
936-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
937-
zend_array_destroy(ht);
942+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
943+
if (!GC_REFCOUNT(ht)) {
944+
zend_array_destroy(ht);
945+
}
938946
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
939947
if (EG(exception)) {
940948
ZVAL_UNDEF(EX_VAR(opline->result.var));
@@ -961,8 +969,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim
961969
execute_data = EG(current_execute_data);
962970
opline = EX(opline);
963971
zend_use_resource_as_offset(dim);
964-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
965-
zend_array_destroy(ht);
972+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
973+
if (!GC_REFCOUNT(ht)) {
974+
zend_array_destroy(ht);
975+
}
966976
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
967977
if (EG(exception)) {
968978
ZVAL_UNDEF(EX_VAR(opline->result.var));

0 commit comments

Comments
 (0)