Fix array object clobbering by user error handler

Fixes oss-fuss #41605 and #41610
This commit is contained in:
Dmitry Stogov 2021-12-03 13:35:28 +03:00
parent 2515e788bc
commit 1d054b3fa7
4 changed files with 938 additions and 141 deletions

View File

@ -2318,9 +2318,15 @@ fetch_from_array:
ZVAL_UNDEF(result);
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
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 (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
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);

View File

@ -1203,8 +1203,16 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array):
}
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
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(container);
GC_ADDREF(obj);
dim = ZVAL_UNDEFINED_OP2();
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
ZEND_VM_C_GOTO(assign_dim_op_ret_null);
}
} else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
dim++;
}
zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC);
@ -2575,12 +2583,32 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
}
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
value = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R);
if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
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);
FREE_OP_DATA();

File diff suppressed because it is too large Load Diff

View File

@ -1290,9 +1290,23 @@ static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval
zval *retval;
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
retval = Z_OBJ_HT_P(object_ptr)->read_dimension(Z_OBJ_P(object_ptr), dim, type, result);
zend_object *obj = Z_OBJ_P(object_ptr);
if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) {
const zend_op *opline = EG(current_execute_data)->opline;
GC_ADDREF(obj);
zend_jit_undefined_op_helper(opline->op2.var);
dim = &EG(uninitialized_zval);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
ZVAL_NULL(result);
return;
}
}
retval = obj->handlers->read_dimension(obj, dim, type, result);
if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
zend_class_entry *ce = Z_OBJCE_P(object_ptr);
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));
@ -1303,7 +1317,7 @@ static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval
retval = result;
}
if (Z_TYPE_P(retval) != IS_OBJECT) {
zend_class_entry *ce = Z_OBJCE_P(object_ptr);
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)) {
@ -1356,7 +1370,49 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zva
static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result)
{
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) && EXPECTED(dim != NULL)) {
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
zend_object *obj = Z_OBJ_P(object_ptr);
if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
const zend_op *opline = EG(current_execute_data)->opline;
GC_ADDREF(obj);
zend_jit_undefined_op_helper(opline->op2.var);
dim = &EG(uninitialized_zval);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
if (result) {
ZVAL_NULL(result);
}
return;
}
}
if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
const zend_op *op_data = EG(current_execute_data)->opline + 1;
ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
GC_ADDREF(obj);
zend_jit_undefined_op_helper(op_data->op1.var);
value = &EG(uninitialized_zval);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
if (result) {
ZVAL_NULL(result);
}
return;
}
} else {
ZVAL_DEREF(value);
}
Z_OBJ_HT_P(object_ptr)->write_dimension(obj, dim, value);
if (result) {
if (EXPECTED(!EG(exception))) {
ZVAL_COPY(result, value);
} else {
ZVAL_UNDEF(result);
}
}
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) && EXPECTED(dim != NULL)) {
zend_assign_to_string_offset(object_ptr, dim, value, result);
return;
}
@ -1374,17 +1430,7 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim
value = &EG(uninitialized_zval);
}
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
ZVAL_DEREF(value);
Z_OBJ_HT_P(object_ptr)->write_dimension(Z_OBJ_P(object_ptr), dim, value);
if (result) {
if (EXPECTED(!EG(exception))) {
ZVAL_COPY(result, value);
} else {
ZVAL_UNDEF(result);
}
}
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
zend_throw_error(NULL, "[] operator not supported for strings");
if (result) {
ZVAL_UNDEF(result);
@ -1400,16 +1446,29 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim
static void ZEND_FASTCALL zend_jit_assign_dim_op_helper(zval *container, zval *dim, zval *value, binary_op_type binary_op)
{
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
zval *object = container;
zval *property = dim;
zend_object *obj = Z_OBJ_P(container);
zval *z;
zval rv, res;
z = Z_OBJ_HT_P(object)->read_dimension(Z_OBJ_P(object), property, BP_VAR_R, &rv);
if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) {
const zend_op *opline = EG(current_execute_data)->opline;
GC_ADDREF(obj);
zend_jit_undefined_op_helper(opline->op2.var);
dim = &EG(uninitialized_zval);
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
zend_objects_store_del(obj);
//??? if (retval) {
//??? ZVAL_NULL(retval);
//??? }
return;
}
}
z = obj->handlers->read_dimension(obj, dim, BP_VAR_R, &rv);
if (z != NULL) {
if (binary_op(&res, Z_ISREF_P(z) ? Z_REFVAL_P(z) : z, value) == SUCCESS) {
Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), property, &res);
obj->handlers->write_dimension(obj, dim, &res);
}
if (z == &rv) {
zval_ptr_dtor(&rv);