]> granicus.if.org Git - php/commitdiff
Add flag to forbid dynamic property creation on internal classes
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 14 May 2020 09:51:36 +0000 (11:51 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 24 Jun 2020 09:52:36 +0000 (11:52 +0200)
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.

13 files changed:
Zend/tests/weakrefs/weakmap_error_conditions.phpt
Zend/tests/weakrefs/weakrefs_003.phpt
Zend/zend_compile.h
Zend/zend_object_handlers.c
Zend/zend_weakrefs.c
ext/curl/interface.c
ext/curl/multi.c
ext/curl/share.c
ext/enchant/enchant.c
ext/sysvsem/sysvsem.c
ext/sysvshm/sysvshm.c
ext/xml/xml.c
ext/zlib/zlib.c

index 75a824f4226ffa58de0de7573144522f9a5aefee..2516c0443fd14f40904f6985f37ec502a424daa4 100644 (file)
@@ -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
index 66dc93237216de4e5d960376a3873968681ed805..42fe0560a71f7b449f07fd47f303738ac53d5c7b 100644 (file)
@@ -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";
@@ -16,28 +14,15 @@ try {
     var_dump($ex->getMessage());
 }
 
-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"
index e11e6902b9d53aab76ca534ddac464879f24966f..a3fb59bd3e6d470a07356e0dc5a1a0530fc63887 100644 (file)
@@ -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  |     |     |     */
 /*                                                        |     |     |     */
index 763c86e21af2d4939d78457b880a6f7a08d20e28..5b6f65496501279cb1756603bb629cb48929d25e 100644 (file)
@@ -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);
                        }
index b633a57e9fd5e1d171112a0eb185f2888ba21cd7..0e94617dbfa5f83ccffd3da953499bf550b3b632 100644 (file)
@@ -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;
 }
 /* }}} */
 
index af58d37ff58772594c4adbf51ad54cf15c91c5d7..d8b408d19db74ceab49f45e7a177df22699b9057 100644 (file)
@@ -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;
index 7f2f98ad291125138ef61bb7a79b0aed1c8d1b58..ee3c034523ad5c66e5ca750f8584da3e25aadb10 100644 (file)
@@ -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;
index 60eddc2f50333a3c09e8e17a8a2c4b572b56985b..aa6b27f458c11a51aab48ac9b91f2d07ec1cb162 100644 (file)
@@ -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;
index 19918e8a84da60545ac58c20bd16012cf0d30ad2..09d6c914af3f9386a6187eab196562887b039050 100644 (file)
@@ -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;
index 3ba235e5da4fb69ab6dcd0daf0f408730947d1a5..c6c607cda02dba80e54044f52f8391d6f783bb6a 100644 (file)
@@ -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;
index c92f6f9b887b0a23d68874f5071eb47da3923a21..79fe3b20b0ed67a47cc6003ff82a86e6295c6830 100644 (file)
@@ -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;
index f626dd1636781a4ffff3d3bc4df1d7fc0c37aff3..9c2b5db2a86a2f75aa13807da667b957b79ac508 100644 (file)
@@ -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;
 
index aa3be3a513926e7fe7a0f94fbe2fbf763f4dcc35..2ced8031f3f1ada4340de2ff817992aedb437d15 100644 (file)
@@ -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;