Skip to content

Commit 7184783

Browse files
committed
Prevent array modification if it's captured by user error handler during
index conversion Fixes oss-fuzz #44235
1 parent 8e2406c commit 7184783

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
@@ -2147,8 +2147,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht
21472147
GC_ADDREF(ht);
21482148
}
21492149
zend_undefined_offset(lval);
2150-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
2151-
zend_array_destroy(ht);
2150+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2151+
if (!GC_REFCOUNT(ht)) {
2152+
zend_array_destroy(ht);
2153+
}
21522154
return NULL;
21532155
}
21542156
if (EG(exception)) {
@@ -2169,8 +2171,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht,
21692171
/* Key may be released while throwing the undefined index warning. */
21702172
zend_string_addref(offset);
21712173
zend_undefined_index(offset);
2172-
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
2173-
zend_array_destroy(ht);
2174+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2175+
if (!GC_REFCOUNT(ht)) {
2176+
zend_array_destroy(ht);
2177+
}
21742178
retval = NULL;
21752179
} else if (EG(exception)) {
21762180
retval = NULL;
@@ -2319,6 +2323,80 @@ static zend_never_inline zend_uchar slow_index_convert(HashTable *ht, const zval
23192323
}
23202324
}
23212325

2326+
static zend_never_inline zend_uchar slow_index_convert_w(HashTable *ht, const zval *dim, zend_value *value EXECUTE_DATA_DC)
2327+
{
2328+
switch (Z_TYPE_P(dim)) {
2329+
case IS_UNDEF: {
2330+
/* The array may be destroyed while throwing the notice.
2331+
* Temporarily increase the refcount to detect this situation. */
2332+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2333+
GC_ADDREF(ht);
2334+
}
2335+
ZVAL_UNDEFINED_OP2();
2336+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2337+
if (!GC_REFCOUNT(ht)) {
2338+
zend_array_destroy(ht);
2339+
}
2340+
return IS_NULL;
2341+
}
2342+
if (EG(exception)) {
2343+
return IS_NULL;
2344+
}
2345+
ZEND_FALLTHROUGH;
2346+
}
2347+
case IS_NULL:
2348+
value->str = ZSTR_EMPTY_ALLOC();
2349+
return IS_STRING;
2350+
case IS_DOUBLE:
2351+
value->lval = zend_dval_to_lval(Z_DVAL_P(dim));
2352+
if (!zend_is_long_compatible(Z_DVAL_P(dim), value->lval)) {
2353+
/* The array may be destroyed while throwing the notice.
2354+
* Temporarily increase the refcount to detect this situation. */
2355+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2356+
GC_ADDREF(ht);
2357+
}
2358+
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
2359+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2360+
if (!GC_REFCOUNT(ht)) {
2361+
zend_array_destroy(ht);
2362+
}
2363+
return IS_NULL;
2364+
}
2365+
if (EG(exception)) {
2366+
return IS_NULL;
2367+
}
2368+
}
2369+
return IS_LONG;
2370+
case IS_RESOURCE:
2371+
/* The array may be destroyed while throwing the notice.
2372+
* Temporarily increase the refcount to detect this situation. */
2373+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
2374+
GC_ADDREF(ht);
2375+
}
2376+
zend_use_resource_as_offset(dim);
2377+
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
2378+
if (!GC_REFCOUNT(ht)) {
2379+
zend_array_destroy(ht);
2380+
}
2381+
return IS_NULL;
2382+
}
2383+
if (EG(exception)) {
2384+
return IS_NULL;
2385+
}
2386+
value->lval = Z_RES_HANDLE_P(dim);
2387+
return IS_LONG;
2388+
case IS_FALSE:
2389+
value->lval = 0;
2390+
return IS_LONG;
2391+
case IS_TRUE:
2392+
value->lval = 1;
2393+
return IS_LONG;
2394+
default:
2395+
zend_illegal_offset();
2396+
return IS_NULL;
2397+
}
2398+
}
2399+
23222400
static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type EXECUTE_DATA_DC)
23232401
{
23242402
zval *retval = NULL;
@@ -2380,8 +2458,13 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
23802458
goto try_again;
23812459
} else {
23822460
zend_value val;
2383-
zend_uchar t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
2461+
zend_uchar t;
23842462

2463+
if (type != BP_VAR_W && type != BP_VAR_RW) {
2464+
t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
2465+
} else {
2466+
t = slow_index_convert_w(ht, dim, &val EXECUTE_DATA_CC);
2467+
}
23852468
if (t == IS_STRING) {
23862469
offset_key = val.str;
23872470
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)