Skip to content

Commit cbc0b1a

Browse files
committed
Fix array clobering by user error handler
Fixes oss-fuzz #42234
1 parent 230de77 commit cbc0b1a

File tree

6 files changed

+317
-478
lines changed

6 files changed

+317
-478
lines changed

Zend/tests/objects_034.phpt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
Array object clobbering by user error handler
33
--FILE--
44
<?php
5-
class A {
5+
class A implements ArrayAccess {
6+
public function &offsetGet($n) {
7+
}
8+
public function offsetSet($n, $v) {
9+
}
10+
public function offsetUnset($n) {
11+
}
12+
public function offsetExists($n) {
13+
}
614
}
715

816
set_error_handler(function () {

Zend/tests/objects_035.phpt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Array object clobbering by user error handler
3+
--FILE--
4+
<?php
5+
class A implements ArrayAccess {
6+
public function &offsetGet($n) {
7+
}
8+
public function offsetSet($n, $v) {
9+
}
10+
public function offsetUnset($n) {
11+
}
12+
public function offsetExists($n) {
13+
}
14+
}
15+
16+
set_error_handler(function () {
17+
$GLOBALS['a'] = null;
18+
});
19+
20+
$a = new A;
21+
$a[$c];
22+
?>
23+
DONE
24+
--EXPECT--
25+
DONE

Zend/zend_execute.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,9 +1264,9 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_string_offset
12641264
zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset));
12651265
}
12661266

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

12711271
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
12721272
ZVAL_COPY(EX_VAR(opline->result.var), value);
@@ -1300,12 +1300,14 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
13001300
zval *value;
13011301
zval *z;
13021302
zval rv, res;
1303+
zend_object *obj = Z_OBJ_P(object);
13031304

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

13071309
if (zend_binary_op(&res, z, value OPLINE_CC) == SUCCESS) {
1308-
Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), property, &res);
1310+
obj->handlers->write_dimension(obj, property, &res);
13091311
}
13101312
if (z == &rv) {
13111313
zval_ptr_dtor(&rv);
@@ -1321,6 +1323,9 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *
13211323
}
13221324
}
13231325
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
1326+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1327+
zend_objects_store_del(obj);
1328+
}
13241329
}
13251330

13261331
static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value OPLINE_DC EXECUTE_DATA_DC)
@@ -2317,22 +2322,17 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
23172322
}
23182323
ZVAL_UNDEF(result);
23192324
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2325+
zend_object *obj = Z_OBJ_P(container);
2326+
GC_ADDREF(obj);
23202327
if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
2321-
zend_object *obj = Z_OBJ_P(container);
2322-
GC_ADDREF(obj);
23232328
dim = ZVAL_UNDEFINED_OP2();
2324-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2325-
zend_objects_store_del(obj);
2326-
ZVAL_NULL(result);
2327-
return;
2328-
}
23292329
} else if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
23302330
dim++;
23312331
}
2332-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2332+
retval = obj->handlers->read_dimension(obj, dim, type, result);
23332333

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

23372337
ZVAL_NULL(result);
23382338
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
@@ -2343,7 +2343,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
23432343
retval = result;
23442344
}
23452345
if (Z_TYPE_P(retval) != IS_OBJECT) {
2346-
zend_class_entry *ce = Z_OBJCE_P(container);
2346+
zend_class_entry *ce = obj->ce;
23472347
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
23482348
}
23492349
} else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
@@ -2356,6 +2356,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
23562356
ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
23572357
ZVAL_UNDEF(result);
23582358
}
2359+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2360+
zend_objects_store_del(obj);
2361+
}
23592362
} else {
23602363
if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
23612364
if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
@@ -2508,13 +2511,16 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
25082511
ZVAL_CHAR(result, c);
25092512
}
25102513
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2514+
zend_object *obj = Z_OBJ_P(container);
2515+
2516+
GC_ADDREF(obj);
25112517
if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
25122518
dim = ZVAL_UNDEFINED_OP2();
25132519
}
25142520
if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
25152521
dim++;
25162522
}
2517-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2523+
retval = obj->handlers->read_dimension(obj, dim, type, result);
25182524

25192525
ZEND_ASSERT(result != NULL);
25202526
if (retval) {
@@ -2526,6 +2532,9 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
25262532
} else {
25272533
ZVAL_NULL(result);
25282534
}
2535+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2536+
zend_objects_store_del(obj);
2537+
}
25292538
} else {
25302539
if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
25312540
container = ZVAL_UNDEFINED_OP1();

Zend/zend_vm_def.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2583,35 +2583,29 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
25832583
}
25842584
}
25852585
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
2586+
zend_object *obj = Z_OBJ_P(object_ptr);
2587+
2588+
GC_ADDREF(obj);
25862589
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
25872590
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) {
2588-
zend_object *obj = Z_OBJ_P(object_ptr);
2589-
GC_ADDREF(obj);
25902591
dim = ZVAL_UNDEFINED_OP2();
2591-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2592-
zend_objects_store_del(obj);
2593-
ZEND_VM_C_GOTO(assign_dim_error);
2594-
}
25952592
} else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
25962593
dim++;
25972594
}
25982595

25992596
value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R);
26002597
if (OP_DATA_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) {
2601-
zend_object *obj = Z_OBJ_P(object_ptr);
2602-
GC_ADDREF(obj);
26032598
value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
2604-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2605-
zend_objects_store_del(obj);
2606-
ZEND_VM_C_GOTO(assign_dim_error);
2607-
}
26082599
} else if (OP_DATA_TYPE & (IS_CV|IS_VAR)) {
26092600
ZVAL_DEREF(value);
26102601
}
26112602

2612-
zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);
2603+
zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC);
26132604

26142605
FREE_OP_DATA();
2606+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2607+
zend_objects_store_del(obj);
2608+
}
26152609
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
26162610
if (OP2_TYPE == IS_UNUSED) {
26172611
zend_use_new_element_for_string();

0 commit comments

Comments
 (0)