]> granicus.if.org Git - php/commitdiff
Fixed bug #71940 (Unserialize crushes on restore object reference)
authorXinchen Hui <laruence@gmail.com>
Thu, 7 Apr 2016 05:56:55 +0000 (13:56 +0800)
committerXinchen Hui <laruence@gmail.com>
Thu, 7 Apr 2016 05:56:55 +0000 (13:56 +0800)
NEWS
ext/standard/tests/serialize/bug71940.phpt [new file with mode: 0644]
ext/standard/var_unserializer.c
ext/standard/var_unserializer.re

diff --git a/NEWS b/NEWS
index e3d2d26426455a26553f3509b372588eb27fd41c..56629821b267599fff4faea7917ae27938b9ffa1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,8 @@ PHP                                                                        NEWS
   . Fixed bug #52339 (SPL autoloader breaks class_exists()). (Nikita)
 
 - Standard:
+  . Fixed bug #71940 (Unserialize crushes on restore object reference).
+    (Laruence)
   . Fixed bug #71969 (str_replace returns an incorrect resulting array after
     a foreach by reference). (Laruence)
   . Fixed bug #71891 (header_register_callback() and
diff --git a/ext/standard/tests/serialize/bug71940.phpt b/ext/standard/tests/serialize/bug71940.phpt
new file mode 100644 (file)
index 0000000..c391ba6
--- /dev/null
@@ -0,0 +1,67 @@
+--TEST--
+Bug #71940 (Unserialize crushes on restore object reference)
+--FILE--
+<?php
+
+class Identity
+{
+    private $role;
+
+    public function __construct($role)
+    {
+        $this->role = $role;
+    }
+}
+
+class Entry implements \Serializable
+{
+    private $identity;
+
+    public function __construct(Identity $identity)
+    {
+        $this->identity = $identity;
+    }
+
+    public function serialize()
+    {
+        return serialize(array($this->identity));
+    }
+
+    public function unserialize($serialized)
+    {
+        list($this->identity) = unserialize($serialized);
+    }
+}
+
+$identity = new Identity('test');
+$identityRef = &$identity;
+
+$entry1 = new Entry($identity);
+$entry2 = new Entry($identityRef);
+
+$serialized = serialize([$entry1, $entry2]);
+print_r(unserialize($serialized));
+
+?>
+--EXPECTF--
+Array
+(
+    [0] => Entry Object
+        (
+            [identity:Entry:private] => Identity Object
+                (
+                    [role:Identity:private] => test
+                )
+
+        )
+
+    [1] => Entry Object
+        (
+            [identity:Entry:private] => Identity Object
+                (
+                    [role:Identity:private] => test
+                )
+
+        )
+
+)
index 443a42cca31688376e62a5ce26e5dd2b48452fea..03ba75087377823b12130fdcd778d78c66908176 100644 (file)
@@ -574,7 +574,7 @@ yy2:
        yych = *(YYMARKER = ++YYCURSOR);
        if (yych == ':') goto yy95;
 yy3:
-#line 884 "ext/standard/var_unserializer.re"
+#line 886 "ext/standard/var_unserializer.re"
        { return 0; }
 #line 580 "ext/standard/var_unserializer.c"
 yy4:
@@ -619,7 +619,7 @@ yy13:
        goto yy3;
 yy14:
        ++YYCURSOR;
-#line 878 "ext/standard/var_unserializer.re"
+#line 880 "ext/standard/var_unserializer.re"
        {
        /* this is the case where we have less data than planned */
        php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
@@ -655,7 +655,7 @@ yy20:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 733 "ext/standard/var_unserializer.re"
+#line 735 "ext/standard/var_unserializer.re"
        {
        size_t len, len2, len3, maxlen;
        zend_long elements;
@@ -825,7 +825,7 @@ yy27:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 726 "ext/standard/var_unserializer.re"
+#line 728 "ext/standard/var_unserializer.re"
        {
     if (!var_hash) return 0;
 
@@ -865,19 +865,21 @@ yy34:
        }
 
        array_init_size(rval, elements);
-//??? we can't convert from packed to hash during unserialization, because
-//??? reference to some zvals might be keept in var_hash (to support references)
        if (elements) {
+               /* we can't convert from packed to hash during unserialization, because
+                  reference to some zvals might be keept in var_hash (to support references) */
                zend_hash_real_init(Z_ARRVAL_P(rval), 0);
        }
 
+       /* We should keep an reference to rval to prevent it from being dtor */
+       var_push_dtor(var_hash, rval);
        if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
                return 0;
        }
 
        return finish_nested_data(UNSERIALIZE_PASSTHRU);
 }
-#line 881 "ext/standard/var_unserializer.c"
+#line 883 "ext/standard/var_unserializer.c"
 yy39:
        yych = *++YYCURSOR;
        if (yych == '+') goto yy40;
@@ -932,7 +934,7 @@ yy41:
        ZVAL_STR(rval, str);
        return 1;
 }
-#line 936 "ext/standard/var_unserializer.c"
+#line 938 "ext/standard/var_unserializer.c"
 yy46:
        yych = *++YYCURSOR;
        if (yych == '+') goto yy47;
@@ -985,7 +987,7 @@ yy48:
        ZVAL_STRINGL(rval, str, len);
        return 1;
 }
-#line 989 "ext/standard/var_unserializer.c"
+#line 991 "ext/standard/var_unserializer.c"
 yy53:
        yych = *++YYCURSOR;
        if (yych <= '/') {
@@ -1082,7 +1084,7 @@ use_double:
        ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
        return 1;
 }
-#line 1086 "ext/standard/var_unserializer.c"
+#line 1088 "ext/standard/var_unserializer.c"
 yy65:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1157,7 +1159,7 @@ yy73:
 
        return 1;
 }
-#line 1161 "ext/standard/var_unserializer.c"
+#line 1163 "ext/standard/var_unserializer.c"
 yy76:
        yych = *++YYCURSOR;
        if (yych == 'N') goto yy73;
@@ -1210,7 +1212,7 @@ yy79:
        ZVAL_LONG(rval, parse_iv(start + 2));
        return 1;
 }
-#line 1214 "ext/standard/var_unserializer.c"
+#line 1216 "ext/standard/var_unserializer.c"
 yy83:
        yych = *++YYCURSOR;
        if (yych <= '/') goto yy18;
@@ -1224,7 +1226,7 @@ yy83:
        ZVAL_BOOL(rval, parse_iv(start + 2));
        return 1;
 }
-#line 1228 "ext/standard/var_unserializer.c"
+#line 1230 "ext/standard/var_unserializer.c"
 yy87:
        ++YYCURSOR;
 #line 573 "ext/standard/var_unserializer.re"
@@ -1233,7 +1235,7 @@ yy87:
        ZVAL_NULL(rval);
        return 1;
 }
-#line 1237 "ext/standard/var_unserializer.c"
+#line 1239 "ext/standard/var_unserializer.c"
 yy89:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1281,7 +1283,7 @@ yy91:
 
        return 1;
 }
-#line 1285 "ext/standard/var_unserializer.c"
+#line 1287 "ext/standard/var_unserializer.c"
 yy95:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1330,9 +1332,9 @@ yy97:
 
        return 1;
 }
-#line 1334 "ext/standard/var_unserializer.c"
+#line 1336 "ext/standard/var_unserializer.c"
 }
-#line 886 "ext/standard/var_unserializer.re"
+#line 888 "ext/standard/var_unserializer.re"
 
 
        return 0;
index abd1f28faedf4b6071f31e30a32bb5233d1e1ab4..02478167f021aec4079ae922edd15db8291fcd0e 100644 (file)
@@ -710,12 +710,14 @@ use_double:
        }
 
        array_init_size(rval, elements);
-//??? we can't convert from packed to hash during unserialization, because
-//??? reference to some zvals might be keept in var_hash (to support references)
        if (elements) {
+               /* we can't convert from packed to hash during unserialization, because
+                  reference to some zvals might be keept in var_hash (to support references) */
                zend_hash_real_init(Z_ARRVAL_P(rval), 0);
        }
 
+       /* We should keep an reference to rval to prevent it from being dtor */
+       var_push_dtor(var_hash, rval);
        if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
                return 0;
        }