Skip to content

Commit

Permalink
Prevent array modification if it's captured by user error handler during
Browse files Browse the repository at this point in the history
index conversion

Fixes oss-fuzz #44235
  • Loading branch information
dstogov committed Feb 1, 2022
1 parent 8e2406c commit 7184783
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 15 deletions.
18 changes: 18 additions & 0 deletions Zend/tests/array_offset_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Capturing array in user error handler during index conversion
--FILE--
<?php
set_error_handler(function($code, $msg) {
echo "Err: $msg\n";
$GLOBALS[''] = $GLOBALS['y'];
});
function x(&$s){
$s[100000000000000000000] = 1;
}
x($y);
var_dump($y);
?>
--EXPECT--
Err: Implicit conversion from float 1.0E+20 to int loses precision
array(0) {
}
93 changes: 88 additions & 5 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -2147,8 +2147,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht
GC_ADDREF(ht);
}
zend_undefined_offset(lval);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
return NULL;
}
if (EG(exception)) {
Expand All @@ -2169,8 +2171,10 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht,
/* Key may be released while throwing the undefined index warning. */
zend_string_addref(offset);
zend_undefined_index(offset);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
retval = NULL;
} else if (EG(exception)) {
retval = NULL;
Expand Down Expand Up @@ -2319,6 +2323,80 @@ static zend_never_inline zend_uchar slow_index_convert(HashTable *ht, const zval
}
}

static zend_never_inline zend_uchar slow_index_convert_w(HashTable *ht, const zval *dim, zend_value *value EXECUTE_DATA_DC)
{
switch (Z_TYPE_P(dim)) {
case IS_UNDEF: {
/* 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);
}
ZVAL_UNDEFINED_OP2();
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
return IS_NULL;
}
if (EG(exception)) {
return IS_NULL;
}
ZEND_FALLTHROUGH;
}
case IS_NULL:
value->str = ZSTR_EMPTY_ALLOC();
return IS_STRING;
case IS_DOUBLE:
value->lval = zend_dval_to_lval(Z_DVAL_P(dim));
if (!zend_is_long_compatible(Z_DVAL_P(dim), value->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_incompatible_double_to_long_error(Z_DVAL_P(dim));
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
return IS_NULL;
}
if (EG(exception)) {
return IS_NULL;
}
}
return IS_LONG;
case IS_RESOURCE:
/* 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_use_resource_as_offset(dim);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
return IS_NULL;
}
if (EG(exception)) {
return IS_NULL;
}
value->lval = Z_RES_HANDLE_P(dim);
return IS_LONG;
case IS_FALSE:
value->lval = 0;
return IS_LONG;
case IS_TRUE:
value->lval = 1;
return IS_LONG;
default:
zend_illegal_offset();
return IS_NULL;
}
}

static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type EXECUTE_DATA_DC)
{
zval *retval = NULL;
Expand Down Expand Up @@ -2380,8 +2458,13 @@ static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht
goto try_again;
} else {
zend_value val;
zend_uchar t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
zend_uchar t;

if (type != BP_VAR_W && type != BP_VAR_RW) {
t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);
} else {
t = slow_index_convert_w(ht, dim, &val EXECUTE_DATA_CC);
}
if (t == IS_STRING) {
offset_key = val.str;
goto str_index;
Expand Down
30 changes: 20 additions & 10 deletions ext/opcache/jit/zend_jit_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,10 @@ static int ZEND_FASTCALL zend_jit_undefined_op_helper_write(HashTable *ht, uint3
GC_ADDREF(ht);
}
zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
return 0;
}
return EG(exception) == NULL;
Expand Down Expand Up @@ -808,8 +810,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di
execute_data = EG(current_execute_data);
opline = EX(opline);
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
if (EG(exception)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
Expand All @@ -836,8 +840,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di
execute_data = EG(current_execute_data);
opline = EX(opline);
zend_use_resource_as_offset(dim);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
if (EG(exception)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
Expand Down Expand Up @@ -933,8 +939,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim
execute_data = EG(current_execute_data);
opline = EX(opline);
zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
if (EG(exception)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
Expand All @@ -961,8 +969,10 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim
execute_data = EG(current_execute_data);
opline = EX(opline);
zend_use_resource_as_offset(dim);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
zend_array_destroy(ht);
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
if (!GC_REFCOUNT(ht)) {
zend_array_destroy(ht);
}
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
if (EG(exception)) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
Expand Down

0 comments on commit 7184783

Please sign in to comment.