Skip to content

Commit 7607582

Browse files
committed
Merge branch 'PHP-8.0' into PHP-8.1
* PHP-8.0: Fix array clobering by user error handler
2 parents f8f0a65 + cbc0b1a commit 7607582

File tree

6 files changed

+318
-476
lines changed

6 files changed

+318
-476
lines changed

Zend/tests/objects_034.phpt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
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): mixed {
7+
return null;
8+
}
9+
public function offsetSet($n, $v): void {
10+
}
11+
public function offsetUnset($n): void {
12+
}
13+
public function offsetExists($n): bool {
14+
return false;
15+
}
616
}
717

818
set_error_handler(function () {

Zend/tests/objects_035.phpt

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

Zend/zend_execute.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,9 +1394,9 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_string_offset
13941394
zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset));
13951395
}
13961396

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

14011401
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
14021402
ZVAL_COPY(EX_VAR(opline->result.var), value);
@@ -1431,6 +1431,7 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zend_object *obj, zv
14311431
zval *z;
14321432
zval rv, res;
14331433

1434+
GC_ADDREF(obj);
14341435
value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1);
14351436
if ((z = obj->handlers->read_dimension(obj, property, BP_VAR_R, &rv)) != NULL) {
14361437

@@ -1451,6 +1452,9 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zend_object *obj, zv
14511452
}
14521453
}
14531454
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
1455+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1456+
zend_objects_store_del(obj);
1457+
}
14541458
}
14551459

14561460
static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value OPLINE_DC EXECUTE_DATA_DC)
@@ -2464,22 +2468,17 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
24642468
}
24652469
ZVAL_UNDEF(result);
24662470
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2471+
zend_object *obj = Z_OBJ_P(container);
2472+
GC_ADDREF(obj);
24672473
if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
2468-
zend_object *obj = Z_OBJ_P(container);
2469-
GC_ADDREF(obj);
24702474
dim = ZVAL_UNDEFINED_OP2();
2471-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2472-
zend_objects_store_del(obj);
2473-
ZVAL_NULL(result);
2474-
return;
2475-
}
24762475
} else if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
24772476
dim++;
24782477
}
2479-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2478+
retval = obj->handlers->read_dimension(obj, dim, type, result);
24802479

24812480
if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
2482-
zend_class_entry *ce = Z_OBJCE_P(container);
2481+
zend_class_entry *ce = obj->ce;
24832482

24842483
ZVAL_NULL(result);
24852484
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
@@ -2490,7 +2489,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
24902489
retval = result;
24912490
}
24922491
if (Z_TYPE_P(retval) != IS_OBJECT) {
2493-
zend_class_entry *ce = Z_OBJCE_P(container);
2492+
zend_class_entry *ce = obj->ce;
24942493
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
24952494
}
24962495
} else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
@@ -2503,6 +2502,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
25032502
ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
25042503
ZVAL_UNDEF(result);
25052504
}
2505+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2506+
zend_objects_store_del(obj);
2507+
}
25062508
} else {
25072509
if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
25082510
if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
@@ -2670,13 +2672,16 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
26702672
ZVAL_CHAR(result, c);
26712673
}
26722674
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2675+
zend_object *obj = Z_OBJ_P(container);
2676+
2677+
GC_ADDREF(obj);
26732678
if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
26742679
dim = ZVAL_UNDEFINED_OP2();
26752680
}
26762681
if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
26772682
dim++;
26782683
}
2679-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2684+
retval = obj->handlers->read_dimension(obj, dim, type, result);
26802685

26812686
ZEND_ASSERT(result != NULL);
26822687
if (retval) {
@@ -2688,6 +2693,9 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
26882693
} else {
26892694
ZVAL_NULL(result);
26902695
}
2696+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2697+
zend_objects_store_del(obj);
2698+
}
26912699
} else {
26922700
if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
26932701
container = ZVAL_UNDEFINED_OP1();

Zend/zend_vm_def.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,35 +2612,29 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
26122612
}
26132613
}
26142614
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
2615+
zend_object *obj = Z_OBJ_P(object_ptr);
2616+
2617+
GC_ADDREF(obj);
26152618
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
26162619
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) {
2617-
zend_object *obj = Z_OBJ_P(object_ptr);
2618-
GC_ADDREF(obj);
26192620
dim = ZVAL_UNDEFINED_OP2();
2620-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2621-
zend_objects_store_del(obj);
2622-
ZEND_VM_C_GOTO(assign_dim_error);
2623-
}
26242621
} else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
26252622
dim++;
26262623
}
26272624

26282625
value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R);
26292626
if (OP_DATA_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) {
2630-
zend_object *obj = Z_OBJ_P(object_ptr);
2631-
GC_ADDREF(obj);
26322627
value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
2633-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2634-
zend_objects_store_del(obj);
2635-
ZEND_VM_C_GOTO(assign_dim_error);
2636-
}
26372628
} else if (OP_DATA_TYPE & (IS_CV|IS_VAR)) {
26382629
ZVAL_DEREF(value);
26392630
}
26402631

2641-
zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);
2632+
zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC);
26422633

26432634
FREE_OP_DATA();
2635+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2636+
zend_objects_store_del(obj);
2637+
}
26442638
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
26452639
if (OP2_TYPE == IS_UNUSED) {
26462640
zend_use_new_element_for_string();

0 commit comments

Comments
 (0)