Skip to content

Commit d9926a1

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Fix array clobering by user error handler
2 parents 2337c05 + 7607582 commit d9926a1

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
@@ -1380,9 +1380,9 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_string_offset
13801380
zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset));
13811381
}
13821382

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

13871387
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
13881388
ZVAL_COPY(EX_VAR(opline->result.var), value);
@@ -1417,6 +1417,7 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zend_object *obj, zv
14171417
zval *z;
14181418
zval rv, res;
14191419

1420+
GC_ADDREF(obj);
14201421
value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1);
14211422
if ((z = obj->handlers->read_dimension(obj, property, BP_VAR_R, &rv)) != NULL) {
14221423

@@ -1437,6 +1438,9 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zend_object *obj, zv
14371438
}
14381439
}
14391440
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
1441+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1442+
zend_objects_store_del(obj);
1443+
}
14401444
}
14411445

14421446
static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value OPLINE_DC EXECUTE_DATA_DC)
@@ -2390,22 +2394,17 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
23902394
}
23912395
ZVAL_UNDEF(result);
23922396
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2397+
zend_object *obj = Z_OBJ_P(container);
2398+
GC_ADDREF(obj);
23932399
if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
2394-
zend_object *obj = Z_OBJ_P(container);
2395-
GC_ADDREF(obj);
23962400
dim = ZVAL_UNDEFINED_OP2();
2397-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2398-
zend_objects_store_del(obj);
2399-
ZVAL_NULL(result);
2400-
return;
2401-
}
24022401
} else if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
24032402
dim++;
24042403
}
2405-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2404+
retval = obj->handlers->read_dimension(obj, dim, type, result);
24062405

24072406
if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
2408-
zend_class_entry *ce = Z_OBJCE_P(container);
2407+
zend_class_entry *ce = obj->ce;
24092408

24102409
ZVAL_NULL(result);
24112410
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
@@ -2416,7 +2415,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
24162415
retval = result;
24172416
}
24182417
if (Z_TYPE_P(retval) != IS_OBJECT) {
2419-
zend_class_entry *ce = Z_OBJCE_P(container);
2418+
zend_class_entry *ce = obj->ce;
24202419
zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
24212420
}
24222421
} else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
@@ -2429,6 +2428,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *
24292428
ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
24302429
ZVAL_UNDEF(result);
24312430
}
2431+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2432+
zend_objects_store_del(obj);
2433+
}
24322434
} else {
24332435
if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
24342436
if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
@@ -2596,13 +2598,16 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
25962598
ZVAL_CHAR(result, c);
25972599
}
25982600
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
2601+
zend_object *obj = Z_OBJ_P(container);
2602+
2603+
GC_ADDREF(obj);
25992604
if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
26002605
dim = ZVAL_UNDEFINED_OP2();
26012606
}
26022607
if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
26032608
dim++;
26042609
}
2605-
retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result);
2610+
retval = obj->handlers->read_dimension(obj, dim, type, result);
26062611

26072612
ZEND_ASSERT(result != NULL);
26082613
if (retval) {
@@ -2614,6 +2619,9 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
26142619
} else {
26152620
ZVAL_NULL(result);
26162621
}
2622+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2623+
zend_objects_store_del(obj);
2624+
}
26172625
} else {
26182626
if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
26192627
container = ZVAL_UNDEFINED_OP1();

Zend/zend_vm_def.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,35 +2621,29 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
26212621
}
26222622
}
26232623
if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
2624+
zend_object *obj = Z_OBJ_P(object_ptr);
2625+
2626+
GC_ADDREF(obj);
26242627
dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
26252628
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) {
2626-
zend_object *obj = Z_OBJ_P(object_ptr);
2627-
GC_ADDREF(obj);
26282629
dim = ZVAL_UNDEFINED_OP2();
2629-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2630-
zend_objects_store_del(obj);
2631-
ZEND_VM_C_GOTO(assign_dim_error);
2632-
}
26332630
} else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) {
26342631
dim++;
26352632
}
26362633

26372634
value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R);
26382635
if (OP_DATA_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) {
2639-
zend_object *obj = Z_OBJ_P(object_ptr);
2640-
GC_ADDREF(obj);
26412636
value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC);
2642-
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2643-
zend_objects_store_del(obj);
2644-
ZEND_VM_C_GOTO(assign_dim_error);
2645-
}
26462637
} else if (OP_DATA_TYPE & (IS_CV|IS_VAR)) {
26472638
ZVAL_DEREF(value);
26482639
}
26492640

2650-
zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC);
2641+
zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC);
26512642

26522643
FREE_OP_DATA();
2644+
if (UNEXPECTED(GC_DELREF(obj) == 0)) {
2645+
zend_objects_store_del(obj);
2646+
}
26532647
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
26542648
if (OP2_TYPE == IS_UNUSED) {
26552649
zend_use_new_element_for_string();

0 commit comments

Comments
 (0)