diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-05-14 11:51:36 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-06-24 11:52:36 +0200 |
commit | 653e4ea1c57def2d5cd75b7da9e3943a841b7d6c (patch) | |
tree | 479717f0e9a8eb24e733990512304fc7209ca45d | |
parent | 2a28589c7def80ecc7bb3417a8853df6b7860b46 (diff) | |
download | php-git-653e4ea1c57def2d5cd75b7da9e3943a841b7d6c.tar.gz |
Add flag to forbid dynamic property creation on internal classes
While performing resource -> object migrations, we're adding
defensive classes that are final, non-serializable and non-clonable
(unless they are, of course). This path adds a ZEND_ACC_NO_DYNAMIC_PROPERTIES
flag, that also forbids the creation of dynamic properties on these objects.
This is a subset of #3931 and targeted at internal usage only
(though may be extended to userland at some point in the future).
It's already possible to achieve this (what the removed
WeakRef/WeakMap code does), but there's some caveats: First, this
simple approach is only possible if the class has no declared
properties, otherwise it's necessary to special-case those
properties. Second, it's easy to make it overly strict, e.g. by
forbidding isset($obj->prop) as well. And finally, it requires a
lot of boilerplate code for each class.
Closes GH-5572.
-rw-r--r-- | Zend/tests/weakrefs/weakmap_error_conditions.phpt | 30 | ||||
-rw-r--r-- | Zend/tests/weakrefs/weakrefs_003.phpt | 33 | ||||
-rw-r--r-- | Zend/zend_compile.h | 5 | ||||
-rw-r--r-- | Zend/zend_object_handlers.c | 15 | ||||
-rw-r--r-- | Zend/zend_weakrefs.c | 47 | ||||
-rw-r--r-- | ext/curl/interface.c | 2 | ||||
-rw-r--r-- | ext/curl/multi.c | 2 | ||||
-rw-r--r-- | ext/curl/share.c | 2 | ||||
-rw-r--r-- | ext/enchant/enchant.c | 4 | ||||
-rw-r--r-- | ext/sysvsem/sysvsem.c | 2 | ||||
-rw-r--r-- | ext/sysvshm/sysvshm.c | 2 | ||||
-rw-r--r-- | ext/xml/xml.c | 2 | ||||
-rw-r--r-- | ext/zlib/zlib.c | 4 |
13 files changed, 53 insertions, 97 deletions
diff --git a/Zend/tests/weakrefs/weakmap_error_conditions.phpt b/Zend/tests/weakrefs/weakmap_error_conditions.phpt index 75a824f422..2516c0443f 100644 --- a/Zend/tests/weakrefs/weakmap_error_conditions.phpt +++ b/Zend/tests/weakrefs/weakmap_error_conditions.phpt @@ -41,13 +41,17 @@ try { echo $e->getMessage(), "\n"; } +var_dump($map->prop); +var_dump(isset($map->prop)); +unset($map->prop); + try { $map->prop = 1; } catch (Error $e) { echo $e->getMessage(), "\n"; } try { - var_dump($map->prop); + $map->prop[] = 1; } catch (Error $e) { echo $e->getMessage(), "\n"; } @@ -56,16 +60,6 @@ try { } catch (Error $e) { echo $e->getMessage(), "\n"; } -try { - isset($map->prop); -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -try { - unset($map->prop); -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} try { serialize($map); @@ -79,7 +73,7 @@ try { } ?> ---EXPECT-- +--EXPECTF-- WeakMap key must be an object WeakMap key must be an object WeakMap key must be an object @@ -87,10 +81,12 @@ WeakMap key must be an object Cannot append to WeakMap Cannot append to WeakMap Object stdClass#2 not contained in WeakMap -WeakMap objects do not support properties -WeakMap objects do not support properties -WeakMap objects do not support property references -WeakMap objects do not support properties -WeakMap objects do not support properties + +Warning: Undefined property: WeakMap::$prop in %s on line %d +NULL +bool(false) +Cannot create dynamic property WeakMap::$prop +Cannot create dynamic property WeakMap::$prop +Cannot create dynamic property WeakMap::$prop Serialization of 'WeakMap' is not allowed Unserialization of 'WeakMap' is not allowed diff --git a/Zend/tests/weakrefs/weakrefs_003.phpt b/Zend/tests/weakrefs/weakrefs_003.phpt index 66dc932372..42fe0560a7 100644 --- a/Zend/tests/weakrefs/weakrefs_003.phpt +++ b/Zend/tests/weakrefs/weakrefs_003.phpt @@ -4,11 +4,9 @@ WeakReference object handlers <?php $wr = WeakReference::create(new stdClass); -try { - $wr->disallow; -} catch (Error $ex) { - var_dump($ex->getMessage()); -} +var_dump($wr->disallow); +var_dump(isset($wr->disallow)); +unset($wr->disallow); try { $wr->disallow = "writes"; @@ -17,27 +15,14 @@ try { } try { - isset($wr->disallow); -} catch (Error $ex) { - var_dump($ex->getMessage()); -} - -try { - unset($wr->disallow); -} catch (Error $ex) { - var_dump($ex->getMessage()); -} - -try { $disallow = &$wr->disallowed; } catch (Error $ex) { var_dump($ex->getMessage()); } ?> ---EXPECT-- -string(47) "WeakReference objects do not support properties" -string(47) "WeakReference objects do not support properties" -string(47) "WeakReference objects do not support properties" -string(47) "WeakReference objects do not support properties" -string(56) "WeakReference objects do not support property references" - +--EXPECTF-- +Warning: Undefined property: WeakReference::$disallow in %s on line %d +NULL +bool(false) +string(55) "Cannot create dynamic property WeakReference::$disallow" +string(57) "Cannot create dynamic property WeakReference::$disallowed" diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index e11e6902b9..a3fb59bd3e 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -238,7 +238,7 @@ typedef struct _zend_oparray_context { /* op_array or class is preloaded | | | */ #define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */ /* | | | */ -/* Class Flags (unused: 13, 14, 15, 24...) | | | */ +/* Class Flags (unused: 14, 15, 24...) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -260,6 +260,9 @@ typedef struct _zend_oparray_context { /* Class constants updated | | | */ #define ZEND_ACC_CONSTANTS_UPDATED (1 << 12) /* X | | | */ /* | | | */ +/* Objects of this class may not have dynamic properties | | | */ +#define ZEND_ACC_NO_DYNAMIC_PROPERTIES (1 << 13) /* X | | | */ +/* | | | */ /* User class has methods with static variables | | | */ #define ZEND_HAS_STATIC_IN_METHODS (1 << 16) /* X | | | */ /* | | | */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 763c86e21a..5b6f654965 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -261,6 +261,12 @@ static ZEND_COLD zend_never_inline void zend_bad_property_name(void) /* {{{ */ } /* }}} */ +static ZEND_COLD zend_never_inline void zend_forbidden_dynamic_property( + zend_class_entry *ce, zend_string *member) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", + ZSTR_VAL(ce->name), ZSTR_VAL(member)); +} + static zend_always_inline uintptr_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot, zend_property_info **info_ptr) /* {{{ */ { zval *zv; @@ -772,6 +778,11 @@ write_std_property: ZVAL_COPY_VALUE(variable_ptr, value); } else { + if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(zobj->ce, name); + variable_ptr = &EG(error_zval); + goto exit; + } if (!zobj->properties) { rebuild_object_properties(zobj); } @@ -935,6 +946,10 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam } if (EXPECTED(!zobj->ce->__get) || UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) { + if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_forbidden_dynamic_property(zobj->ce, name); + return &EG(error_zval); + } if (UNEXPECTED(!zobj->properties)) { rebuild_object_properties(zobj); } diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index b633a57e9f..0e94617dbf 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -234,39 +234,6 @@ static void zend_weakref_free(zend_object *zo) { zend_object_std_dtor(&wr->std); } -#define zend_weakref_unsupported(object, thing) \ - zend_throw_error(NULL, "%s objects do not support " thing, ZSTR_VAL(object->ce->name)); - -static ZEND_COLD zval* zend_weakref_no_write(zend_object *object, zend_string *member, zval *value, void **rtc) { - zend_weakref_unsupported(object, "properties"); - - return &EG(uninitialized_zval); -} - -static ZEND_COLD zval* zend_weakref_no_read(zend_object *object, zend_string *member, int type, void **rtc, zval *rv) { - if (!EG(exception)) { - zend_weakref_unsupported(object, "properties"); - } - - return &EG(uninitialized_zval); -} - -static ZEND_COLD zval *zend_weakref_no_read_ptr(zend_object *object, zend_string *member, int type, void **rtc) { - zend_weakref_unsupported(object, "property references"); - return NULL; -} - -static ZEND_COLD int zend_weakref_no_isset(zend_object *object, zend_string *member, int hse, void **rtc) { - if (hse != 2) { - zend_weakref_unsupported(object, "properties"); - } - return 0; -} - -static ZEND_COLD void zend_weakref_no_unset(zend_object *object, zend_string *member, void **rtc) { - zend_weakref_unsupported(object, "properties"); -} - ZEND_COLD ZEND_METHOD(WeakReference, __construct) { zend_throw_error(NULL, @@ -615,7 +582,7 @@ void zend_register_weakref_ce(void) /* {{{ */ INIT_CLASS_ENTRY(ce, "WeakReference", class_WeakReference_methods); zend_ce_weakref = zend_register_internal_class(&ce); - zend_ce_weakref->ce_flags |= ZEND_ACC_FINAL; + zend_ce_weakref->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; zend_ce_weakref->create_object = zend_weakref_new; zend_ce_weakref->serialize = zend_class_serialize_deny; @@ -625,16 +592,11 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_weakref_handlers.offset = XtOffsetOf(zend_weakref, std); zend_weakref_handlers.free_obj = zend_weakref_free; - zend_weakref_handlers.read_property = zend_weakref_no_read; - zend_weakref_handlers.write_property = zend_weakref_no_write; - zend_weakref_handlers.has_property = zend_weakref_no_isset; - zend_weakref_handlers.unset_property = zend_weakref_no_unset; - zend_weakref_handlers.get_property_ptr_ptr = zend_weakref_no_read_ptr; zend_weakref_handlers.clone_obj = NULL; INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods); zend_ce_weakmap = zend_register_internal_class(&ce); - zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL; + zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; zend_ce_weakmap->create_object = zend_weakmap_create_object; zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator; @@ -656,11 +618,6 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_weakmap_handlers.get_properties_for = zend_weakmap_get_properties_for; zend_weakmap_handlers.get_gc = zend_weakmap_get_gc; zend_weakmap_handlers.clone_obj = zend_weakmap_clone_obj; - zend_weakmap_handlers.read_property = zend_weakref_no_read; - zend_weakmap_handlers.write_property = zend_weakref_no_write; - zend_weakmap_handlers.has_property = zend_weakref_no_isset; - zend_weakmap_handlers.unset_property = zend_weakref_no_unset; - zend_weakmap_handlers.get_property_ptr_ptr = zend_weakref_no_read_ptr; } /* }}} */ diff --git a/ext/curl/interface.c b/ext/curl/interface.c index af58d37ff5..d8b408d19d 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1193,7 +1193,7 @@ PHP_MINIT_FUNCTION(curl) zend_class_entry ce; INIT_CLASS_ENTRY(ce, "CurlHandle", class_CurlHandle_methods); curl_ce = zend_register_internal_class(&ce); - curl_ce->ce_flags |= ZEND_ACC_FINAL; + curl_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; curl_ce->create_object = curl_create_object; curl_ce->serialize = zend_class_serialize_deny; curl_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 7f2f98ad29..ee3c034523 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -597,7 +597,7 @@ void curl_multi_register_class(const zend_function_entry *method_entries) { zend_class_entry ce_multi; INIT_CLASS_ENTRY(ce_multi, "CurlMultiHandle", method_entries); curl_multi_ce = zend_register_internal_class(&ce_multi); - curl_multi_ce->ce_flags |= ZEND_ACC_FINAL; + curl_multi_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; curl_multi_ce->create_object = curl_multi_create_object; curl_multi_ce->serialize = zend_class_serialize_deny; curl_multi_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/curl/share.c b/ext/curl/share.c index 60eddc2f50..aa6b27f458 100644 --- a/ext/curl/share.c +++ b/ext/curl/share.c @@ -173,7 +173,7 @@ void curl_share_register_class(const zend_function_entry *method_entries) { zend_class_entry ce_share; INIT_CLASS_ENTRY(ce_share, "CurlShareHandle", method_entries); curl_share_ce = zend_register_internal_class(&ce_share); - curl_share_ce->ce_flags |= ZEND_ACC_FINAL; + curl_share_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; curl_share_ce->create_object = curl_share_create_object; curl_share_ce->serialize = &zend_class_serialize_deny; curl_share_ce->unserialize = &zend_class_unserialize_deny; diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index 19918e8a84..09d6c914af 100644 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -192,7 +192,7 @@ PHP_MINIT_FUNCTION(enchant) INIT_CLASS_ENTRY(bce, "EnchantBroker", class_EnchantBroker_methods); enchant_broker_ce = zend_register_internal_class(&bce); - enchant_broker_ce->ce_flags |= ZEND_ACC_FINAL; + enchant_broker_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; enchant_broker_ce->create_object = enchant_broker_create_object; enchant_broker_ce->serialize = zend_class_serialize_deny; enchant_broker_ce->unserialize = zend_class_unserialize_deny; @@ -204,7 +204,7 @@ PHP_MINIT_FUNCTION(enchant) INIT_CLASS_ENTRY(dce, "EnchantDictionary", class_EnchantDictionary_methods); enchant_dict_ce = zend_register_internal_class(&dce); - enchant_dict_ce->ce_flags |= ZEND_ACC_FINAL; + enchant_dict_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; enchant_dict_ce->create_object = enchant_dict_create_object; enchant_dict_ce->serialize = zend_class_serialize_deny; enchant_dict_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index 3ba235e5da..c6c607cda0 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -156,7 +156,7 @@ PHP_MINIT_FUNCTION(sysvsem) zend_class_entry ce; INIT_CLASS_ENTRY(ce, "SysvSemaphore", class_SysvSemaphore_methods); sysvsem_ce = zend_register_internal_class(&ce); - sysvsem_ce->ce_flags |= ZEND_ACC_FINAL; + sysvsem_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; sysvsem_ce->create_object = sysvsem_create_object; sysvsem_ce->serialize = zend_class_serialize_deny; sysvsem_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c index c92f6f9b88..79fe3b20b0 100644 --- a/ext/sysvshm/sysvshm.c +++ b/ext/sysvshm/sysvshm.c @@ -104,7 +104,7 @@ PHP_MINIT_FUNCTION(sysvshm) zend_class_entry ce; INIT_CLASS_ENTRY(ce, "SysvSharedMemory", class_SysvSharedMemory_methods); sysvshm_ce = zend_register_internal_class(&ce); - sysvshm_ce->ce_flags |= ZEND_ACC_FINAL; + sysvshm_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; sysvshm_ce->create_object = sysvshm_create_object; sysvshm_ce->serialize = zend_class_serialize_deny; sysvshm_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/xml/xml.c b/ext/xml/xml.c index f626dd1636..9c2b5db2a8 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -257,7 +257,7 @@ PHP_MINIT_FUNCTION(xml) INIT_CLASS_ENTRY(ce, "XmlParser", class_XMLParser_methods); xml_parser_ce = zend_register_internal_class(&ce); xml_parser_ce->create_object = xml_parser_create_object; - xml_parser_ce->ce_flags |= ZEND_ACC_FINAL; + xml_parser_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; xml_parser_ce->serialize = zend_class_serialize_deny; xml_parser_ce->unserialize = zend_class_unserialize_deny; diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index aa3be3a513..2ced8031f3 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -1359,7 +1359,7 @@ static PHP_MINIT_FUNCTION(zlib) zend_class_entry inflate_ce; INIT_CLASS_ENTRY(inflate_ce, "InflateContext", class_InflateContext_methods); inflate_context_ce = zend_register_internal_class(&inflate_ce); - inflate_context_ce->ce_flags |= ZEND_ACC_FINAL; + inflate_context_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; inflate_context_ce->create_object = inflate_context_create_object; inflate_context_ce->serialize = zend_class_serialize_deny; inflate_context_ce->unserialize = zend_class_unserialize_deny; @@ -1373,7 +1373,7 @@ static PHP_MINIT_FUNCTION(zlib) zend_class_entry deflate_ce; INIT_CLASS_ENTRY(deflate_ce, "DeflateContext", class_DeflateContext_methods); deflate_context_ce = zend_register_internal_class(&deflate_ce); - deflate_context_ce->ce_flags |= ZEND_ACC_FINAL; + deflate_context_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; deflate_context_ce->create_object = deflate_context_create_object; deflate_context_ce->serialize = zend_class_serialize_deny; deflate_context_ce->unserialize = zend_class_unserialize_deny; |