Skip to content

Commit

Permalink
Fix array clobering by user error handler
Browse files Browse the repository at this point in the history
Fixes oss-fuzz #42234
  • Loading branch information
dstogov committed Dec 13, 2021
1 parent 230de77 commit cbc0b1a
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 478 deletions.
10 changes: 9 additions & 1 deletion Zend/tests/objects_034.phpt
Expand Up @@ -2,7 +2,15 @@
Array object clobbering by user error handler
--FILE--
<?php
class A {
class A implements ArrayAccess {
public function &offsetGet($n) {
}
public function offsetSet($n, $v) {
}
public function offsetUnset($n) {
}
public function offsetExists($n) {
}
}

set_error_handler(function () {
Expand Down
25 changes: 25 additions & 0 deletions Zend/tests/objects_035.phpt
@@ -0,0 +1,25 @@
--TEST--
Array object clobbering by user error handler
--FILE--
<?php
class A implements ArrayAccess {
public function &offsetGet($n) {
}
public function offsetSet($n, $v) {
}
public function offsetUnset($n) {
}
public function offsetExists($n) {
}
}

set_error_handler(function () {
$GLOBALS['a'] = null;
});

$a = new A;
$a[$c];
?>
DONE
--EXPECT--
DONE
39 changes: 24 additions & 15 deletions Zend/zend_execute.c
Expand Up @@ -1264,9 +1264,9 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_string_offset
zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset));
}

static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC)
static zend_never_inline void zend_assign_to_object_dim(zend_object *obj, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC)
{
Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), dim, value);
obj->handlers->write_dimension(obj, dim, value);

if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
Expand Down Expand Up @@ -1300,12 +1300,14 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
zval *value;
zval *z;
zval rv, res;
zend_object *obj = Z_OBJ_P(object);

GC_ADDREF(obj);
value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1);
if ((z = Z_OBJ_HT_P(object)->read_dimension(Z_OBJ_P(object), property, BP_VAR_R, &rv)) != NULL) {
if ((z = obj->handlers->read_dimension(obj, property, BP_VAR_R, &rv)) != NULL) {

if (zend_binary_op(&res, z, value OPLINE_CC) == SUCCESS) {
Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), property, &res);
obj->handlers->write_dimension(obj, property, &res);
}
if (z == &rv) {
zval_ptr_dtor(&rv);
Expand All @@ -1321,6 +1323,9 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
}
}
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
}
}

static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value OPLINE_DC EXECUTE_DATA_DC)
Expand Down Expand Up @@ -2317,22 +2322,17 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
}
ZVAL_UNDEF(result);
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
zend_object *obj = Z_OBJ_P(container);
GC_ADDREF(obj);
if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
zend_object *obj = Z_OBJ_P(container);
GC_ADDREF(obj);
dim = ZVAL_UNDEFINED_OP2();
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
ZVAL_NULL(result);
return;
}
} else if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
dim++;
}
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
retval = obj->handlers->read_dimension(obj, dim, type, result);

if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
zend_class_entry *ce = Z_OBJCE_P(container);
zend_class_entry *ce = obj->ce;

ZVAL_NULL(result);
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
Expand All @@ -2343,7 +2343,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
retval = result;
}
if (Z_TYPE_P(retval) != IS_OBJECT) {
zend_class_entry *ce = Z_OBJCE_P(container);
zend_class_entry *ce = obj->ce;
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
}
} else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
Expand All @@ -2356,6 +2356,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
ZVAL_UNDEF(result);
}
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
}
} else {
if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
Expand Down Expand Up @@ -2508,13 +2511,16 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
ZVAL_CHAR(result, c);
}
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
zend_object *obj = Z_OBJ_P(container);

GC_ADDREF(obj);
if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
dim = ZVAL_UNDEFINED_OP2();
}
if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
dim++;
}
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
retval = obj->handlers->read_dimension(obj, dim, type, result);

ZEND_ASSERT(result != NULL);
if (retval) {
Expand All @@ -2526,6 +2532,9 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
} else {
ZVAL_NULL(result);
}
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
}
} else {
if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
container = ZVAL_UNDEFINED_OP1();
Expand Down
20 changes: 7 additions & 13 deletions Zend/zend_vm_def.h
Expand Up @@ -2583,35 +2583,29 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
zend_object *obj = Z_OBJ_P(object_ptr);

GC_ADDREF(obj);
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) {
zend_object *obj = Z_OBJ_P(object_ptr);
GC_ADDREF(obj);
dim = ZVAL_UNDEFINED_OP2();
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
ZEND_VM_C_GOTO(assign_dim_error);
}
} else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
dim++;
}

value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R);
if (OP_DATA_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) {
zend_object *obj = Z_OBJ_P(object_ptr);
GC_ADDREF(obj);
value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
ZEND_VM_C_GOTO(assign_dim_error);
}
} else if (OP_DATA_TYPE & (IS_CV|IS_VAR)) {
ZVAL_DEREF(value);
}

zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);
zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC);

FREE_OP_DATA();
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
}
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
if (OP2_TYPE == IS_UNUSED) {
zend_use_new_element_for_string();
Expand Down

0 comments on commit cbc0b1a

Please sign in to comment.