]> granicus.if.org Git - php/commitdiff
Fix #49649 - Handle property visibility changes on unserialization
authorPedro Magalhães <mail@pmmaga.net>
Fri, 21 Apr 2017 17:37:27 +0000 (19:37 +0200)
committerJoe Watkins <krakjoe@php.net>
Mon, 10 Jul 2017 06:27:09 +0000 (07:27 +0100)
NEWS
ext/standard/tests/serialize/bug49649.phpt [new file with mode: 0644]
ext/standard/tests/serialize/bug49649_1.phpt [new file with mode: 0644]
ext/standard/tests/serialize/bug49649_2.phpt [new file with mode: 0644]
ext/standard/var_unserializer.re

diff --git a/NEWS b/NEWS
index d9c1e5baf7f57488c4ed4ddfddff0229d3671193..c88a6c59e3dbfcb030ed8ad21a5841bcef40d458 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ PHP                                                                        NEWS
   . Fixed bug #74819 (wddx_deserialize() heap out-of-bound read via
     php_parse_date()). (Derick)
   . Fixed bug #74878 (Data race in ZTS builds). (Nikita)
+  . Fixed bug #49649 (unserialize() doesn't handle changes in property 
+    visibility). (pmmaga)
 
 - Date:
   . Fixed bug #74852 (property_exists returns true on unknown DateInterval 
diff --git a/ext/standard/tests/serialize/bug49649.phpt b/ext/standard/tests/serialize/bug49649.phpt
new file mode 100644 (file)
index 0000000..2e0a505
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+Bug #49649 (unserialize() doesn't handle changes in property visibility) - to public
+--FILE--
+<?php
+
+/**
+ *class Foo
+ *{
+ *     private $private = 1;
+ *     
+ *     protected $protected = 2;
+ *     
+ *     public $public = 3;
+ *     
+ *     public $notThere = 'old';
+ * }
+ *
+ * echo base64_encode(serialize(new Foo()));
+ *
+ * The class above represents the serialized, base64_encoded string below.
+*/
+$serialized = 'TzozOiJGb28iOjQ6e3M6MTI6IgBGb28AcHJpdmF0ZSI7aToxO3M6MTI6IgAqAHByb3RlY3RlZCI7aToyO3M6NjoicHVibGljIjtpOjM7czo4OiJub3RUaGVyZSI7czozOiJvbGQiO30';
+
+class Foo
+{
+       public $public = null;
+
+       public $protected = null;
+
+       public $private = null;
+}
+
+$class = unserialize(base64_decode($serialized));
+var_dump($class);
+--EXPECT--
+object(Foo)#1 (4) {
+  ["public"]=>
+  int(3)
+  ["protected"]=>
+  int(2)
+  ["private"]=>
+  int(1)
+  ["notThere"]=>
+  string(3) "old"
+}
diff --git a/ext/standard/tests/serialize/bug49649_1.phpt b/ext/standard/tests/serialize/bug49649_1.phpt
new file mode 100644 (file)
index 0000000..1ca840a
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+Bug #49649 (unserialize() doesn't handle changes in property visibility) - to protected
+--FILE--
+<?php
+
+/**
+ *class Foo
+ *{
+ *     private $private = 1;
+ *     
+ *     protected $protected = 2;
+ *     
+ *     public $public = 3;
+ *     
+ *     public $notThere = 'old';
+ * }
+ *
+ * echo base64_encode(serialize(new Foo()));
+ *
+ * The class above represents the serialized, base64_encoded string below.
+*/
+$serialized = 'TzozOiJGb28iOjQ6e3M6MTI6IgBGb28AcHJpdmF0ZSI7aToxO3M6MTI6IgAqAHByb3RlY3RlZCI7aToyO3M6NjoicHVibGljIjtpOjM7czo4OiJub3RUaGVyZSI7czozOiJvbGQiO30';
+
+class Foo
+{
+       protected $public = null;
+
+       protected $protected = null;
+
+       protected $private = null;
+}
+
+$class = unserialize(base64_decode($serialized));
+var_dump($class);
+--EXPECT--
+object(Foo)#1 (4) {
+  ["public":protected]=>
+  int(3)
+  ["protected":protected]=>
+  int(2)
+  ["private":protected]=>
+  int(1)
+  ["notThere"]=>
+  string(3) "old"
+}
diff --git a/ext/standard/tests/serialize/bug49649_2.phpt b/ext/standard/tests/serialize/bug49649_2.phpt
new file mode 100644 (file)
index 0000000..e9fe429
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+Bug #49649 (unserialize() doesn't handle changes in property visibility) - to private
+--FILE--
+<?php
+
+/**
+ *class Foo
+ *{
+ *     private $private = 1;
+ *     
+ *     protected $protected = 2;
+ *     
+ *     public $public = 3;
+ *     
+ *     public $notThere = 'old';
+ * }
+ *
+ * echo base64_encode(serialize(new Foo()));
+ *
+ * The class above represents the serialized, base64_encoded string below.
+*/
+$serialized = 'TzozOiJGb28iOjQ6e3M6MTI6IgBGb28AcHJpdmF0ZSI7aToxO3M6MTI6IgAqAHByb3RlY3RlZCI7aToyO3M6NjoicHVibGljIjtpOjM7czo4OiJub3RUaGVyZSI7czozOiJvbGQiO30';
+
+class Foo
+{
+       private $public = null;
+
+       private $protected = null;
+
+       private $private = null;
+}
+
+$class = unserialize(base64_decode($serialized));
+var_dump($class);
+--EXPECT--
+object(Foo)#1 (4) {
+  ["public":"Foo":private]=>
+  int(3)
+  ["protected":"Foo":private]=>
+  int(2)
+  ["private":"Foo":private]=>
+  int(1)
+  ["notThere"]=>
+  string(3) "old"
+}
index 185e62d0f588ae53f237997e0051b36b14cabe65..6e7f76fea2e84b9d8e9d4e608f9ee1bb8af801b0 100644 (file)
@@ -427,14 +427,58 @@ numeric_key:
                } else {
                        if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
 string_key:
-                               if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
-                                       if (Z_TYPE_P(old_data) == IS_INDIRECT) {
-                                               old_data = Z_INDIRECT_P(old_data);
+                               {
+                                       zend_property_info *existing_propinfo;
+                                       zend_string *new_key, *unmangled;
+                                       const char *unmangled_class = NULL; 
+                                       const char *unmangled_prop;
+                                       size_t unmangled_prop_len;
+
+                                       if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) {
+                                               zval_dtor(&key);
+                                               return 0;
+                                       }
+
+                                       unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0);
+                                       if (Z_TYPE_P(rval) == IS_OBJECT
+                                                       && ((existing_propinfo = zend_hash_find_ptr(&Z_OBJCE_P(rval)->properties_info, unmangled)) != NULL) 
+                                                       && (existing_propinfo->flags & ZEND_ACC_PPP_MASK)) {
+                                               if (existing_propinfo->flags & ZEND_ACC_PROTECTED) {
+                                                       new_key = zend_mangle_property_name(
+                                                               "*", 1, ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
+                                                       zend_string_release(unmangled);
+                                               } else if (existing_propinfo->flags & ZEND_ACC_PRIVATE) {
+                                                       if (unmangled_class != NULL && strcmp(unmangled_class, "*") != 0) {
+                                                               new_key = zend_mangle_property_name(
+                                                                       unmangled_class, strlen(unmangled_class),
+                                                                       ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
+                                                                       Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
+                                                       } else {
+                                                               new_key = zend_mangle_property_name(
+                                                                       ZSTR_VAL(existing_propinfo->ce->name), ZSTR_LEN(existing_propinfo->ce->name),
+                                                                       ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
+                                                                       Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
+                                                       }
+                                                       zend_string_release(unmangled);
+                                               } else {
+                                                       ZEND_ASSERT(existing_propinfo->flags & ZEND_ACC_PUBLIC);
+                                                       new_key = unmangled;
+                                               }
+                                               zend_string_release(Z_STR(key));
+                                               Z_STR(key) = new_key;
+                                       } else {
+                                               zend_string_release(unmangled);
+                                       }
+
+                                       if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
+                                               if (Z_TYPE_P(old_data) == IS_INDIRECT) {
+                                                       old_data = Z_INDIRECT_P(old_data);
+                                               }
+                                               var_push_dtor(var_hash, old_data);
+                                               data = zend_hash_update_ind(ht, Z_STR(key), &d);
+                                       } else {
+                                               data = zend_hash_add_new(ht, Z_STR(key), &d);
                                        }
-                                       var_push_dtor(var_hash, old_data);
-                                       data = zend_hash_update_ind(ht, Z_STR(key), &d);
-                               } else {
-                                       data = zend_hash_add_new(ht, Z_STR(key), &d);
                                }
                        } else if (Z_TYPE(key) == IS_LONG) {
                                /* object properties should include no integers */