Skip to content

Commit a0dff6f

Browse files
committed
fix bug #61782 - __clone/__destruct do not match other methods when checking access controls
1 parent 9344bf1 commit a0dff6f

File tree

7 files changed

+66
-38
lines changed

7 files changed

+66
-38
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ PHP NEWS
2020
member of a null object). (Laruence)
2121
. Fixed bug #61978 (Object recursion not detected for classes that implement
2222
JsonSerializable). (Felipe)
23+
. Fixed bug #61782 (__clone/__destruct do not match other methods when checking
24+
access controls). (Stas)
2325
. Fixed bug #61730 (Segfault from array_walk modifying an array passed by
2426
reference). (Laruence)
2527
. Fixed bug #61922 (ZTS build doesn't accept zend.script_encoding config).

Zend/tests/bug61782.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Bug #61782 (__clone/__destruct do not match other methods when checking access controls)
3+
--FILE--
4+
<?php
5+
abstract class BaseClass {
6+
abstract protected function __clone();
7+
}
8+
9+
class MommasBoy extends BaseClass {
10+
protected function __clone() {
11+
echo __METHOD__, "\n";
12+
}
13+
}
14+
15+
class LatchkeyKid extends BaseClass {
16+
public function __construct() {
17+
echo 'In ', __CLASS__, ":\n";
18+
$kid = new MommasBoy();
19+
$kid = clone $kid;
20+
}
21+
public function __clone() {}
22+
}
23+
24+
$obj = new LatchkeyKid();
25+
echo "DONE\n";
26+
--EXPECT--
27+
In LatchkeyKid:
28+
MommasBoy::__clone
29+
DONE

Zend/zend_object_handlers.c

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,24 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
6666
zend_hash_get_current_data_ex(&ce->properties_info, (void**)&prop_info, &pos) == SUCCESS;
6767
zend_hash_move_forward_ex(&ce->properties_info, &pos)) {
6868
if (/*prop_info->ce == ce &&*/
69-
(prop_info->flags & ZEND_ACC_STATIC) == 0 &&
69+
(prop_info->flags & ZEND_ACC_STATIC) == 0 &&
7070
prop_info->offset >= 0 &&
7171
zobj->properties_table[prop_info->offset]) {
7272
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
73-
}
73+
}
7474
}
7575
while (ce->parent && ce->parent->default_properties_count) {
7676
ce = ce->parent;
7777
for (zend_hash_internal_pointer_reset_ex(&ce->properties_info, &pos);
7878
zend_hash_get_current_data_ex(&ce->properties_info, (void**)&prop_info, &pos) == SUCCESS;
7979
zend_hash_move_forward_ex(&ce->properties_info, &pos)) {
8080
if (prop_info->ce == ce &&
81-
(prop_info->flags & ZEND_ACC_STATIC) == 0 &&
82-
(prop_info->flags & ZEND_ACC_PRIVATE) != 0 &&
81+
(prop_info->flags & ZEND_ACC_STATIC) == 0 &&
82+
(prop_info->flags & ZEND_ACC_PRIVATE) != 0 &&
8383
prop_info->offset >= 0 &&
8484
zobj->properties_table[prop_info->offset]) {
8585
zend_hash_quick_add(zobj->properties, prop_info->name, prop_info->name_length+1, prop_info->h, (void**)&zobj->properties_table[prop_info->offset], sizeof(zval*), (void**)&zobj->properties_table[prop_info->offset]);
86-
}
86+
}
8787
}
8888
}
8989
}
@@ -783,7 +783,7 @@ static void zend_std_unset_property(zval *object, zval *member, const zend_liter
783783
property_info = zend_get_property_info_quick(zobj->ce, member, (zobj->ce->__unset != NULL), key TSRMLS_CC);
784784

785785
if (EXPECTED(property_info != NULL) &&
786-
EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) &&
786+
EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) &&
787787
!zobj->properties &&
788788
property_info->offset >= 0 &&
789789
EXPECTED(zobj->properties_table[property_info->offset] != NULL)) {
@@ -815,8 +815,8 @@ static void zend_std_unset_property(zval *object, zval *member, const zend_liter
815815
}
816816
}
817817
}
818-
} else if (EXPECTED(property_info != NULL) &&
819-
EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) &&
818+
} else if (EXPECTED(property_info != NULL) &&
819+
EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) &&
820820
property_info->offset >= 0) {
821821
zobj->properties_table[property_info->offset] = NULL;
822822
}
@@ -960,12 +960,6 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope)
960960
}
961961
/* }}} */
962962

963-
static inline zend_class_entry * zend_get_function_root_class(zend_function *fbc) /* {{{ */
964-
{
965-
return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope;
966-
}
967-
/* }}} */
968-
969963
static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
970964
{
971965
zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
@@ -1143,7 +1137,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, const c
11431137
zend_str_tolower_copy(lc_function_name, function_name_strval, function_name_strlen);
11441138
hash_value = zend_hash_func(lc_function_name, function_name_strlen+1);
11451139
}
1146-
1140+
11471141
if (function_name_strlen == ce->name_length && ce->constructor) {
11481142
lc_class_name = zend_str_tolower_dup(ce->name, ce->name_length);
11491143
/* Only change the method to the constructor if the constructor isn't called __construct
@@ -1178,7 +1172,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, const c
11781172
if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) {
11791173
zend_error_noreturn(E_ERROR, "Cannot call non static method %s::%s() without object", ZEND_FN_SCOPE_NAME(fbc), fbc->common.function_name);
11801174
}
1181-
#endif
1175+
#endif
11821176
if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
11831177
/* No further checks necessary, most common case */
11841178
} else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
@@ -1220,7 +1214,7 @@ ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, const char *p
12201214
{
12211215
zend_property_info *property_info;
12221216
ulong hash_value;
1223-
1217+
12241218
if (UNEXPECTED(!key) ||
12251219
(property_info = CACHED_POLYMORPHIC_PTR(key->cache_slot, ce)) == NULL) {
12261220
if (EXPECTED(key != NULL)) {

Zend/zend_object_handlers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ struct _zend_object_handlers {
146146

147147
extern ZEND_API zend_object_handlers std_object_handlers;
148148

149+
#define zend_get_function_root_class(fbc) \
150+
((fbc)->common.prototype ? (fbc)->common.prototype->common.scope : (fbc)->common.scope)
151+
149152
BEGIN_EXTERN_C()
150153
ZEND_API union _zend_function *zend_std_get_static_method(zend_class_entry *ce, const char *function_name_strval, int function_name_strlen, const struct _zend_literal *key TSRMLS_DC);
151154
ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, const char *property_name, int property_name_len, zend_bool silent, const struct _zend_literal *key TSRMLS_DC);

Zend/zend_objects.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
ZEND_API void zend_object_std_init(zend_object *object, zend_class_entry *ce TSRMLS_DC)
3030
{
31-
object->ce = ce;
31+
object->ce = ce;
3232
object->properties = NULL;
3333
object->properties_table = NULL;
3434
object->guards = NULL;
@@ -38,7 +38,7 @@ ZEND_API void zend_object_std_dtor(zend_object *object TSRMLS_DC)
3838
{
3939
if (object->guards) {
4040
zend_hash_destroy(object->guards);
41-
FREE_HASHTABLE(object->guards);
41+
FREE_HASHTABLE(object->guards);
4242
}
4343
if (object->properties) {
4444
zend_hash_destroy(object->properties);
@@ -74,23 +74,23 @@ ZEND_API void zend_objects_destroy_object(zend_object *object, zend_object_handl
7474
if (object->ce != EG(scope)) {
7575
zend_class_entry *ce = object->ce;
7676

77-
zend_error(EG(in_execution) ? E_ERROR : E_WARNING,
78-
"Call to private %s::__destruct() from context '%s'%s",
79-
ce->name,
80-
EG(scope) ? EG(scope)->name : "",
77+
zend_error(EG(in_execution) ? E_ERROR : E_WARNING,
78+
"Call to private %s::__destruct() from context '%s'%s",
79+
ce->name,
80+
EG(scope) ? EG(scope)->name : "",
8181
EG(in_execution) ? "" : " during shutdown ignored");
8282
return;
8383
}
8484
} else {
8585
/* Ensure that if we're calling a protected function, we're allowed to do so.
8686
*/
87-
if (!zend_check_protected(destructor->common.scope, EG(scope))) {
87+
if (!zend_check_protected(zend_get_function_root_class(destructor), EG(scope))) {
8888
zend_class_entry *ce = object->ce;
8989

90-
zend_error(EG(in_execution) ? E_ERROR : E_WARNING,
91-
"Call to protected %s::__destruct() from context '%s'%s",
92-
ce->name,
93-
EG(scope) ? EG(scope)->name : "",
90+
zend_error(EG(in_execution) ? E_ERROR : E_WARNING,
91+
"Call to protected %s::__destruct() from context '%s'%s",
92+
ce->name,
93+
EG(scope) ? EG(scope)->name : "",
9494
EG(in_execution) ? "" : " during shutdown ignored");
9595
return;
9696
}
@@ -139,7 +139,7 @@ ZEND_API void zend_objects_free_object_storage(zend_object *object TSRMLS_DC)
139139
}
140140

141141
ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_entry *class_type TSRMLS_DC)
142-
{
142+
{
143143
zend_object_value retval;
144144

145145
*object = emalloc(sizeof(zend_object));
@@ -222,7 +222,7 @@ ZEND_API zend_object_value zend_objects_clone_obj(zval *zobject TSRMLS_DC)
222222
zend_object *new_object;
223223
zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
224224

225-
/* assume that create isn't overwritten, so when clone depends on the
225+
/* assume that create isn't overwritten, so when clone depends on the
226226
* overwritten one then it must itself be overwritten */
227227
old_object = zend_objects_get_address(zobject TSRMLS_CC);
228228
new_obj_val = zend_objects_new(&new_object, old_object->ce TSRMLS_CC);

Zend/zend_vm_def.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,7 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
24422442
if (UNEXPECTED(EX(fbc) == NULL)) {
24432443
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
24442444
}
2445-
2445+
24462446
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
24472447
EX(object) = NULL;
24482448
} else {
@@ -2974,7 +2974,7 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, CV)
29742974
catch_ce = CACHED_PTR(opline->op1.literal->cache_slot);
29752975
} else {
29762976
catch_ce = zend_fetch_class_by_name(Z_STRVAL_P(opline->op1.zv), Z_STRLEN_P(opline->op1.zv), opline->op1.literal + 1, ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC);
2977-
2977+
29782978
CACHE_PTR(opline->op1.literal->cache_slot, catch_ce);
29792979
}
29802980
ce = Z_OBJCE_P(EG(exception));
@@ -3426,7 +3426,7 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMP|VAR|UNUSED|CV, ANY)
34263426
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
34273427
/* Ensure that if we're calling a protected function, we're allowed to do so.
34283428
*/
3429-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
3429+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
34303430
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
34313431
}
34323432
}

Zend/zend_vm_execute.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,7 +2436,7 @@ static int ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS
24362436
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
24372437
/* Ensure that if we're calling a protected function, we're allowed to do so.
24382438
*/
2439-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
2439+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
24402440
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
24412441
}
24422442
}
@@ -6850,7 +6850,7 @@ static int ZEND_FASTCALL ZEND_CLONE_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
68506850
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
68516851
/* Ensure that if we're calling a protected function, we're allowed to do so.
68526852
*/
6853-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
6853+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
68546854
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
68556855
}
68566856
}
@@ -11278,7 +11278,7 @@ static int ZEND_FASTCALL ZEND_CLONE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
1127811278
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
1127911279
/* Ensure that if we're calling a protected function, we're allowed to do so.
1128011280
*/
11281-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
11281+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
1128211282
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
1128311283
}
1128411284
}
@@ -21475,7 +21475,7 @@ static int ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARG
2147521475
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
2147621476
/* Ensure that if we're calling a protected function, we're allowed to do so.
2147721477
*/
21478-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
21478+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
2147921479
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
2148021480
}
2148121481
}
@@ -27222,7 +27222,7 @@ static int ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
2722227222
} else if ((clone->common.fn_flags & ZEND_ACC_PROTECTED)) {
2722327223
/* Ensure that if we're calling a protected function, we're allowed to do so.
2722427224
*/
27225-
if (UNEXPECTED(!zend_check_protected(clone->common.scope, EG(scope)))) {
27225+
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), EG(scope)))) {
2722627226
zend_error_noreturn(E_ERROR, "Call to protected %s::__clone() from context '%s'", ce->name, EG(scope) ? EG(scope)->name : "");
2722727227
}
2722827228
}

0 commit comments

Comments
 (0)