Skip to content

Commit 50eec2e

Browse files
committed
Enforce namespace-scoped visibility for property access
Add runtime checks for private(namespace) properties, supporting both regular and asymmetric visibility: * Read access: Check ZEND_ACC_NAMESPACE_PRIVATE flag * Write access: Check ZEND_ACC_NAMESPACE_PRIVATE_SET flag via zend_asymmetric_property_has_set_access() This applies to: * Instance properties (zend_get_property_offset, zend_get_property_info) * Static properties (zend_std_get_static_property_with_info) * Asymmetric visibility (zend_asymmetric_property_has_set_access) Supports asymmetric visibility patterns like: public private(namespace)(set) int $count; Where the property is publicly readable but only writable within the namespace. All checks compare the property's class namespace with the caller's namespace using zend_get_class_namespace() and zend_get_caller_namespace(), denying access when namespaces don't match.
1 parent 4f70a41 commit 50eec2e

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

Zend/zend_object_handlers.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ static zend_always_inline uintptr_t zend_get_property_offset(zend_class_entry *c
391391
property_info = (zend_property_info*)Z_PTR_P(zv);
392392
flags = property_info->flags;
393393

394-
if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
394+
if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED|ZEND_ACC_NAMESPACE_PRIVATE)) {
395395
const zend_class_entry *scope = get_fake_or_executed_scope();
396396

397397
if (property_info->ce != scope) {
@@ -421,6 +421,14 @@ static zend_always_inline uintptr_t zend_get_property_offset(zend_class_entry *c
421421
}
422422
return ZEND_WRONG_PROPERTY_OFFSET;
423423
}
424+
} else if (flags & ZEND_ACC_NAMESPACE_PRIVATE) {
425+
/* Check namespace visibility */
426+
zend_string *property_namespace = zend_get_class_namespace(property_info->ce);
427+
zend_string *caller_namespace = zend_get_caller_namespace();
428+
429+
if (!zend_string_equals(property_namespace, caller_namespace)) {
430+
goto wrong;
431+
}
424432
} else {
425433
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
426434
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
@@ -491,7 +499,7 @@ ZEND_API zend_property_info *zend_get_property_info(const zend_class_entry *ce,
491499
property_info = (zend_property_info*)Z_PTR_P(zv);
492500
flags = property_info->flags;
493501

494-
if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
502+
if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED|ZEND_ACC_NAMESPACE_PRIVATE)) {
495503
const zend_class_entry *scope = get_fake_or_executed_scope();
496504
if (property_info->ce != scope) {
497505
if (flags & ZEND_ACC_CHANGED) {
@@ -516,6 +524,14 @@ ZEND_API zend_property_info *zend_get_property_info(const zend_class_entry *ce,
516524
}
517525
return ZEND_WRONG_PROPERTY_INFO;
518526
}
527+
} else if (flags & ZEND_ACC_NAMESPACE_PRIVATE) {
528+
/* Check namespace visibility */
529+
zend_string *property_namespace = zend_get_class_namespace(property_info->ce);
530+
zend_string *caller_namespace = zend_get_caller_namespace();
531+
532+
if (!zend_string_equals(property_namespace, caller_namespace)) {
533+
goto wrong;
534+
}
519535
} else {
520536
ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
521537
if (UNEXPECTED(!is_protected_compatible_scope(property_info->prototype->ce, scope))) {
@@ -593,6 +609,18 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p
593609
if (prop_info->ce == scope) {
594610
return true;
595611
}
612+
613+
/* Check namespace_private(set) visibility */
614+
if (prop_info->flags & ZEND_ACC_NAMESPACE_PRIVATE_SET) {
615+
zend_string *property_namespace = zend_get_class_namespace(prop_info->ce);
616+
zend_string *caller_namespace = zend_get_caller_namespace();
617+
618+
if (zend_string_equals(property_namespace, caller_namespace)) {
619+
return true;
620+
}
621+
return false;
622+
}
623+
596624
return EXPECTED((prop_info->flags & ZEND_ACC_PROTECTED_SET)
597625
&& is_protected_compatible_scope(prop_info->prototype->ce, scope));
598626
}
@@ -2061,6 +2089,19 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
20612089
return NULL;
20622090
}
20632091
}
2092+
2093+
/* Check namespace visibility */
2094+
if (UNEXPECTED(property_info->flags & ZEND_ACC_NAMESPACE_PRIVATE)) {
2095+
zend_string *property_namespace = zend_get_class_namespace(property_info->ce);
2096+
zend_string *caller_namespace = zend_get_caller_namespace();
2097+
2098+
if (!zend_string_equals(property_namespace, caller_namespace)) {
2099+
if (type != BP_VAR_IS) {
2100+
zend_bad_property_access(property_info, ce, property_name);
2101+
}
2102+
return NULL;
2103+
}
2104+
}
20642105
}
20652106

20662107
if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {

0 commit comments

Comments
 (0)