]> granicus.if.org Git - php/commitdiff
Fix another typed resource issue in unserialization
authorNikita Popov <nikita.ppv@gmail.com>
Mon, 7 Dec 2020 11:33:23 +0000 (12:33 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 7 Dec 2020 11:36:09 +0000 (12:36 +0100)
We also need to discard old entries in the ref_props HT when values
are overwritten.

We should really forbid these kinds of overwrites. I believe they
can only occur in manually crafted serialization strings, and
cause so many problems...

Fixes oss-fuzz #28257.

ext/standard/tests/serialize/typed_property_ref_overwrite2.phpt [new file with mode: 0644]
ext/standard/var_unserializer.re

diff --git a/ext/standard/tests/serialize/typed_property_ref_overwrite2.phpt b/ext/standard/tests/serialize/typed_property_ref_overwrite2.phpt
new file mode 100644 (file)
index 0000000..a408bf3
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Overwriting a typed property that is not yet a reference
+--FILE--
+<?php
+
+class Test {
+    public ?Test $prop;
+}
+$s = <<<'STR'
+O:4:"Test":2:{s:4:"prop";N;s:4:"prop";O:4:"Test":1:{s:4:"prop";R:2;}}
+STR;
+var_dump(unserialize($s));
+
+?>
+--EXPECT--
+object(Test)#1 (1) {
+  ["prop"]=>
+  &object(Test)#2 (1) {
+    ["prop"]=>
+    *RECURSION*
+  }
+}
index 1c787034dd98f25a1343d796fde8ff6680a3424b..1b191a03678a7ce561da89d6f944e3973ef59e91 100644 (file)
@@ -556,9 +556,17 @@ string_key:
                                                /* This is a property with a declaration */
                                                old_data = Z_INDIRECT_P(old_data);
                                                info = zend_get_typed_property_info_for_slot(obj, old_data);
-                                               if (info && Z_ISREF_P(old_data)) {
-                                                       /* If the value is overwritten, remove old type source from ref. */
-                                                       ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(old_data), info);
+                                               if (info) {
+                                                       if (Z_ISREF_P(old_data)) {
+                                                               /* If the value is overwritten, remove old type source from ref. */
+                                                               ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(old_data), info);
+                                                       }
+
+                                                       if ((*var_hash)->ref_props) {
+                                                               /* Remove old entry from ref_props table, if it exists. */
+                                                               zend_hash_index_del(
+                                                                       (*var_hash)->ref_props, (zend_uintptr_t) old_data);
+                                                       }
                                                }
                                                var_push_dtor(var_hash, old_data);
                                                Z_TRY_DELREF_P(old_data);