CVE-2017-12932: Use After Free (CWE-416)
value: ext/standard/var_unserializer.re in PHP 7.0.x through 7.0.22 and 7.1.x through 7.1.8 is prone to a heap use after free while unserializing untrusted data, related to improper use of the hash API for key deletion in a situation with an invalid array size. Exploitation of this issue can have an unspecified impact on the integrity of PHP.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 9.8
Published: 2017-08-18T03:29Z
Repository: https://github.com/php/php-src
Language: Other
Files changed: NEWS, ext/standard/tests/serialize/bug74103.phpt, ext/standard/tests/serialize/bug75054.phpt, ext/standard/var_unserializer.c, ext/standard/var_unserializer.re
Commit date: 2017-08-12 13:11:35 +0200
Commit message: Fixed bug #74103 and bug #75054

Directly fail unserialization when trying to acquire an r/R
reference to an UNDEF HT slot. Previously this left an UNDEF and
later deleted the index/key from the HT.

What actually caused the issue here is a combination of two
factors: First, the key deletion was performed using the hash API,
rather than the symtable API, such that the element was not actually
removed if it used an integral string key. Second, a subsequent
deletion operation, while collecting trailing UNDEF ranges, would
mark the element as available for reuse (leaving a corrupted HT
state with nNumOfElemnts > nNumUsed).

Fix this by failing early and dropping the deletion code.

Diff: diff --git a/NEWS b/NEWS
index 5fc4f75616e..8920376b4d2 100644
--- a/NEWS
+++ b/NEWS
@@ -50,10 +50,16 @@ PHP                                                                        NEWS
   . Fixed bug #75049 (spl_autoload_unregister can't handle
     spl_autoload_functions results). (Laruence)
   . Fixed bug #74669 (Unserialize ArrayIterator broken). (Andrew Nester)
   . Fixed bug #75015 (Crash in recursive iterator destructors). (Julien)
 
+- Standard:
+  . Fixed bug #74103 (heap-use-after-free when unserializing invalid array
+    size). (Nikita)
+  . Fixed bug #75054 (A Denial of Service Vulnerability was found when
+    performing deserialization). (Nikita)
+
 - XMLRPC:
   . Fixed bug #74975 (Incorrect xmlrpc serialization for classes with declared
     properties). (blar)
 
 03 Aug 2017 PHP 7.0.22
diff --git a/ext/standard/tests/serialize/bug74103.phpt b/ext/standard/tests/serialize/bug74103.phpt
new file mode 100644
index 00000000000..3d474b31b1c
--- /dev/null
+++ b/ext/standard/tests/serialize/bug74103.phpt
@@ -0,0 +1,9 @@
+--TEST--
+Bug #74103: heap-use-after-free when unserializing invalid array size
+--FILE--
+<?php
+var_dump(unserialize('a:7:{i:0;i:04;s:1:"a";i:2;i:00009617006;i:4;s:1:"a";i:4;s:1:"a";R:5;s:1:"7";R:3;s:1:"a";R:5;;s:18;}}'));
+?>
+--EXPECTF--
+Notice: unserialize(): Error at offset 68 of 100 bytes in %s on line %d
+bool(false)
diff --git a/ext/standard/tests/serialize/bug75054.phpt b/ext/standard/tests/serialize/bug75054.phpt
new file mode 100644
index 00000000000..51f5692f445
--- /dev/null
+++ b/ext/standard/tests/serialize/bug75054.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #75054: A Denial of Service Vulnerability was found when performing deserialization
+--FILE--
+<?php
+$poc = 'a:9:{i:0;s:4:"0000";i:0;s:4:"0000";i:0;R:2;s:4:"5003";R:2;s:4:"0000";R:2;s:4:"0000";R:2;s:4:"';
+$poc .= "\x06";
+$poc .= '000";R:2;s:4:"0000";d:0;s:4:"0000";a:9:{s:4:"0000";';
+var_dump(unserialize($poc));
+?>
+--EXPECTF--
+Notice: unserialize(): Error at offset 43 of 145 bytes in %s on line %d
+bool(false)
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index c50347d781f..79545a93623 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,6 +1,6 @@
-/* Generated by re2c 0.15.3 */
+/* Generated by re2c 0.16 */
 #line 1 "ext/standard/var_unserializer.re"
 /*
   +----------------------------------------------------------------------+
   | PHP Version 7                                                        |
   +----------------------------------------------------------------------+
@@ -403,20 +403,11 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab
 		if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) {
 			zval_dtor(&key);
 			return 0;
 		}
 
-		if (UNEXPECTED(Z_ISUNDEF_P(data))) {
-			if (Z_TYPE(key) == IS_LONG) {
-				zend_hash_index_del(ht, Z_LVAL(key));
-			} else {
-				zend_hash_del_ind(ht, Z_STR(key));
-			}
-		} else {
-			var_push_dtor(var_hash, data);
-		}
-
+		var_push_dtor(var_hash, data);
 		zval_dtor(&key);
 
 		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
 			(*p)--;
 			return 0;
@@ -579,11 +570,11 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
 	}
 
 	start = cursor;
 
 
-#line 585 "ext/standard/var_unserializer.c"
+#line 576 "ext/standard/var_unserializer.c"
 {
 	YYCTYPE yych;
 	static const unsigned char yybm[] = {
 		  0,   0,   0,   0,   0,   0,   0,   0, 
 		  0,   0,   0,   0,   0,   0,   0,   0, 
@@ -620,798 +611,820 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
 	};
 	if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
 	yych = *YYCURSOR;
 	switch (yych) {
 	case 'C':
-	case 'O':	goto yy13;
+	case 'O':	goto yy4;
 	case 'N':	goto yy5;
-	case 'R':	goto yy2;
-	case 'S':	goto yy10;
-	case 'a':	goto yy11;
-	case 'b':	goto yy6;
-	case 'd':	goto yy8;
-	case 'i':	goto yy7;
+	case 'R':	goto yy6;
+	case 'S':	goto yy7;
+	case 'a':	goto yy8;
+	case 'b':	goto yy9;
+	case 'd':	goto yy10;
+	case 'i':	goto yy11;
 	case 'o':	goto yy12;
-	case 'r':	goto yy4;
-	case 's':	goto yy9;
-	case '}':	goto yy14;
-	default:	goto yy16;
+	case 'r':	goto yy13;
+	case 's':	goto yy14;
+	case '}':	goto yy15;
+	default:	goto yy2;
 	}
 yy2:
-	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy95;
+	++YYCURSOR;
 yy3:
-#line 962 "ext/standard/var_unserializer.re"
+#line 951 "ext/standard/var_unserializer.re"
 	{ return 0; }
-#line 646 "ext/standard/var_unserializer.c"
+#line 636 "ext/standard/var_unserializer.c"
 yy4:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy89;
+	if (yych == ':') goto yy17;
 	goto yy3;
 yy5:
 	yych = *++YYCURSOR;
-	if (yych == ';') goto yy87;
+	if (yych == ';') goto yy19;
 	goto yy3;
 yy6:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy83;
+	if (yych == ':') goto yy21;
 	goto yy3;
 yy7:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy77;
+	if (yych == ':') goto yy22;
 	goto yy3;
 yy8:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy53;
+	if (yych == ':') goto yy23;
 	goto yy3;
 yy9:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy46;
+	if (yych == ':') goto yy24;
 	goto yy3;
 yy10:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy39;
+	if (yych == ':') goto yy25;
 	goto yy3;
 yy11:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy32;
+	if (yych == ':') goto yy26;
 	goto yy3;
 yy12:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy25;
+	if (yych == ':') goto yy27;
 	goto yy3;
 yy13:
 	yych = *(YYMARKER = ++YYCURSOR);
-	if (yych == ':') goto yy17;
+	if (yych == ':') goto yy28;
 	goto yy3;
 yy14:
+	yych = *(YYMARKER = ++YYCURSOR);
+	if (yych == ':') goto yy29;
+	goto yy3;
+yy15:
 	++YYCURSOR;
-#line 956 "ext/standard/var_unserializer.re"
+#line 945 "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");
 	return 0; /* not sure if it should be 0 or 1 here? */
 }
-#line 695 "ext/standard/var_unserializer.c"
-yy16:
-	yych = *++YYCURSOR;
-	goto yy3;
+#line 689 "ext/standard/var_unserializer.c"
 yy17:
 	yych = *++YYCURSOR;
 	if (yybm[0+yych] & 128) {
-		goto yy20;
+		goto yy31;
 	}
-	if (yych == '+') goto yy19;
+	if (yych == '+') goto yy30;
 yy18:
 	YYCURSOR = YYMARKER;
 	goto yy3;
 yy19:
+	++YYCURSOR;
+#line 629 "ext/standard/var_unserializer.re"
+	{
+	*p = YYCURSOR;
+	ZVAL_NULL(rval);
+	return 1;
+}
+#line 707 "ext/standard/var_unserializer.c"
+yy21:
 	yych = *++YYCURSOR;
-	if (yybm[0+yych] & 128) {
-		goto yy20;
+	if (yych <= ',') {
+		if (yych == '+') goto yy33;
+		goto yy18;
+	} else {
+		if (yych <= '-') goto yy33;
+		if (yych <= '/') goto yy18;
+		if (yych <= '9') goto yy34;
+		goto yy18;
 	}
+yy22:
+	yych = *++YYCURSOR;
+	if (yych == '+') goto yy36;
+	if (yych <= '/') goto yy18;
+	if (yych <= '9') goto yy37;
 	goto yy18;
-yy20:
-	++YYCURSOR;
-	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
-	yych = *YYCURSOR;
-	if (yybm[0+yych] & 128) {
-		goto yy20;
-	}
+yy23:
+	yych = *++YYCURSOR;
+	if (yych == '+') goto yy39;
 	if (yych <= '/') goto yy18;
-	if (yych >= ';') goto yy18;
+	if (yych <= '9') goto yy40;
+	goto yy18;
+yy24:
 	yych = *++YYCURSOR;
-	if (yych != '"') goto yy18;
-	++YYCURSOR;
-#line 804 "ext/standard/var_unserializer.re"
-	{
-	size_t len, len2, len3, maxlen;
-	zend_long elements;
-	char *str;
-	zend_string *class_name;
-	zend_class_entry *ce;
-	int incomplete_class = 0;
-
-	int custom_object = 0;
-
-	zval user_func;
-	zval retval;
-	zval args[1];
-
-    if (!var_hash) return 0;
-	if (*start == 'C') {
-		custom_object = 1;
-	}
-
-	len2 = len = parse_uiv(start + 2);
-	maxlen = max - YYCURSOR;
-	if (maxlen < len || len == 0) {
-		*p = start + 2;
-		return 0;
-	}
-
-	str = (char*)YYCURSOR;
-
-	YYCURSOR += len;
-
-	if (*(YYCURSOR) != '"') {
-		*p = YYCURSOR;
-		return 0;
-	}
-	if (*(YYCURSOR+1) != ':') {
-		*p = YYCURSOR+1;
-		return 0;
-	}
-
-	len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
-	if (len3 != len)
-	{
-		*p = YYCURSOR + len3 - len;
-		return 0;
-	}
-
-	class_name = zend_string_init(str, len, 0);
-
-	do {
-		if(!unserialize_allowed_class(class_name, classes)) {
-			incomplete_class = 1;
-			ce = PHP_IC_ENTRY;
-			break;
-		}
-
-		/* Try to find class directly */
-		BG(serialize_lock)++;
-		ce = zend_lookup_class(class_name);
-		if (ce) {
-			BG(serialize_lock)--;
-			if (EG(exception)) {
-				zend_string_release(class_name);
-				return 0;
-			}
-			break;
-		}
-		BG(serialize_lock)--;
-
-		if (EG(exception)) {
-			zend_string_release(class_name);
-			return 0;
-		}
-
-		/* Check for unserialize callback */
-		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
-			incomplete_class = 1;
-			ce = PHP_IC_ENTRY;
-			break;
-		}
-
-		/* Call unserialize callback */
-		ZVAL_STRING(&user_func, PG(unserialize_callback_func));
-
-		ZVAL_STR_COPY(&args[0], class_name);
-		BG(serialize_lock)++;
-		if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
-			BG(serialize_lock)--;
-			if (EG(exception)) {
-				zend_string_release(class_name);
-				zval_ptr_dtor(&user_func);
-				zval_ptr_dtor(&args[0]);
-				return 0;
-			}
-			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
-			incomplete_class = 1;
-			ce = PHP_IC_ENTRY;
-			zval_ptr_dtor(&user_func);
-			zval_ptr_dtor(&args[0]);
-			break;
-		}
-		BG(serialize_lock)--;
-		zval_ptr_dtor(&retval);
-		if (EG(exception)) {
-			zend_string_release(class_name);
-			zval_ptr_dtor(&user_func);
-			zval_ptr_dtor(&args[0]);
-			return 0;
-		}
-
-		/* The callback function may have defined the class */
-		BG(serialize_lock)++;
-		if ((ce = zend_lookup_class(class_name)) == NULL) {
-			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
-			incomplete_class = 1;
-			ce = PHP_IC_ENTRY;
+	if (yych <= '/') goto yy18;
+	if (yych <= '1') goto yy42;
+	goto yy18;
+yy25:
+	yych = *++YYCURSOR;
+	if (yych <= '/') {
+		if (yych <= ',') {
+			if (yych == '+') goto yy43;
+			goto yy18;
+		} else {
+			if (yych <= '-') goto yy44;
+			if (yych <= '.') goto yy45;
+			goto yy18;
 		}
-		BG(serialize_lock)--;
-
-		zval_ptr_dtor(&user_func);
-		zval_ptr_dtor(&args[0]);
-		break;
-	} while (1);
-
-	*p = YYCURSOR;
-
-	if (custom_object) {
-		int ret;
-
-		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
-
-		if (ret && incomplete_class) {
-			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
+	} else {
+		if (yych <= 'I') {
+			if (yych <= '9') goto yy46;
+			if (yych <= 'H') goto yy18;
+			goto yy48;
+		} else {
+			if (yych == 'N') goto yy49;
+			goto yy18;
 		}
-		zend_string_release(class_name);
-		return ret;
 	}
-
-	elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
-
-	if (elements < 0) {
-	   zend_string_release(class_name);
-	   return 0;
+yy26:
+	yych = *++YYCURSOR;
+	if (yych <= ',') {
+		if (yych == '+') goto yy50;
+		goto yy18;
+	} else {
+		if (yych <= '-') goto yy50;
+		if (yych <= '/') goto yy18;
+		if (yych <= '9') goto yy51;
+		goto yy18;
 	}
-
-	if (incomplete_class) {
-		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
+yy27:
+	yych = *++YYCURSOR;
+	if (yych <= ',') {
+		if (yych == '+') goto yy53;
+		goto yy18;
+	} else {
+		if (yych <= '-') goto yy53;
+		if (yych <= '/') goto yy18;
+		if (yych <= '9') goto yy54;
+		goto yy18;
 	}
-	zend_string_release(class_name);
-
-	return object_common2(UNSERIALIZE_PASSTHRU, elements);
-}
-#line 878 "ext/standard/var_unserializer.c"
-yy25:
+yy28:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
-		if (yych != '+') goto yy18;
+		if (yych == '+') goto yy56;
+		goto yy18;
 	} else {
-		if (yych <= '-') goto yy26;
+		if (yych <= '-') goto yy56;
 		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy27;
+		if (yych <= '9') goto yy57;
 		goto yy18;
 	}
-yy26:
+yy29:
 	yych = *++YYCURSOR;
+	if (yych == '+') goto yy59;
 	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
-yy27:
+	if (yych <= '9') goto yy60;
+	goto yy18;
+yy30:
+	yych = *++YYCURSOR;
+	if (yybm[0+yych] & 128) {
+		goto yy31;
+	}
+	goto yy18;
+yy31:
 	++YYCURSOR;
 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
 	yych = *YYCURSOR;
+	if (yybm[0+yych] & 128) {
+		goto yy31;
+	}
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy27;
-	if (yych >= ';') goto yy18;
+	if (yych <= ':') goto yy62;
+	goto yy18;
+yy33:
 	yych = *++YYCURSOR;
-	if (yych != '"') goto yy18;
+	if (yych <= '/') goto yy18;
+	if (yych >= ':') goto yy18;
+yy34:
 	++YYCURSOR;
-#line 793 "ext/standard/var_unserializer.re"
-	{
-	zend_long elements;
-    if (!var_hash) return 0;
-
-	elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
-	if (elements < 0 || elements >= HT_MAX_SIZE) {
-		return 0;
-	}
-	return object_common2(UNSERIALIZE_PASSTHRU, elements);
-}
-#line 914 "ext/standard/var_unserializer.c"
-yy32:
-	yych = *++YYCURSOR;
-	if (yych == '+') goto yy33;
+	if (YYLIMIT <= YYCURSOR) YYFILL(1);
+	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
 	if (yych <= '9') goto yy34;
+	if (yych == ';') goto yy63;
 	goto yy18;
-yy33:
+yy36:
 	yych = *++YYCURSOR;
 	if (yych <= '/') goto yy18;
 	if (yych >= ':') goto yy18;
-yy34:
+yy37:
 	++YYCURSOR;
 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
 	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy34;
-	if (yych >= ';') goto yy18;
+	if (yych <= '9') goto yy37;
+	if (yych <= ':') goto yy65;
+	goto yy18;
+yy39:
 	yych = *++YYCURSOR;
-	if (yych != '{') goto yy18;
+	if (yych <= '/') goto yy18;
+	if (yych >= ':') goto yy18;
+yy40:
 	++YYCURSOR;
-#line 769 "ext/standard/var_unserializer.re"
-	{
-	zend_long elements = parse_iv(start + 2);
-	/* use iv() not uiv() in order to check data range */
-	*p = YYCURSOR;
-    if (!var_hash) return 0;
-
-	if (elements < 0 || elements >= HT_MAX_SIZE) {
-		return 0;
-	}
-
-	array_init_size(rval, elements);
-	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);
+	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+	yych = *YYCURSOR;
+	if (yych <= '/') goto yy18;
+	if (yych <= '9') goto yy40;
+	if (yych <= ':') goto yy66;
+	goto yy18;
+yy42:
+	yych = *++YYCURSOR;
+	if (yych == ';') goto yy67;
+	goto yy18;
+yy43:
+	yych = *++YYCURSOR;
+	if (yych == '.') goto yy45;
+	if (yych <= '/') goto yy18;
+	if (yych <= '9') goto yy46;
+	goto yy18;
+yy44:
+	yych = *++YYCURSOR;
+	if (yych <= '/') {
+		if (yych != '.') goto yy18;
+	} else {
+		if (yych <= '9') goto yy46;
+		if (yych == 'I') goto yy48;
+		goto yy18;
 	}
-
-	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
-		return 0;
+yy45:
+	yych = *++YYCURSOR;
+	if (yych <= '/') goto yy18;
+	if (yych <= '9') goto yy69;
+	goto yy18;
+yy46:
+	++YYCURSOR;
+	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
+	yych = *YYCURSOR;
+	if (yych <= ':') {
+		if (yych <= '.') {
+			if (yych <= '-') goto yy18;
+			goto yy69;
+		} else {
+			if (yych <= '/') goto yy18;
+			if (yych <= '9') goto yy46;
+			goto yy18;
+		}
+	} else {
+		if (yych <= 'E') {
+			if (yych <= ';') goto yy71;
+			if (yych <= 'D') goto yy18;
+			goto yy73;
+		} else {
+			if (yych == 'e') goto yy73;
+			goto yy18;
+		}
 	}
-
-	return finish_nested_data(UNSERIALIZE_PASSTHRU);
-}
-#line 959 "ext/standard/var_unserializer.c"
-yy39:
+yy48:
+	yych = *++YYCURSOR;
+	if (yych == 'N') goto yy74;
+	goto yy18;
+yy49:
+	yych = *++YYCURSOR;
+	if (yych == 'A') goto yy75;
+	goto yy18;
+yy50:
 	yych = *++YYCURSOR;
-	if (yych == '+') goto yy40;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy41;
+	if (yych >= ':') goto yy18;
+yy51:
+	++YYCURSOR;
+	if (YYLIMIT <= YYCURSOR) YYFILL(1);
+	yych = *YYCURSOR;
+	if (yych <= '/') goto yy18;
+	if (yych <= '9') goto yy51;
+	if (yych == ';') goto yy76;
 	goto yy18;
-yy40:
+yy53:
 	yych = *++YYCURSOR;
 	if (yych <= '/') goto yy18;
 	if (yych >= ':') goto yy18;
-yy41:
+yy54:
 	++YYCURSOR;
 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
 	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy41;
-	if (yych >= ';') goto yy18;
+	if (yych <= '9') goto yy54;
+	if (yych <= ':') goto yy78;
+	goto yy18;
+yy56:
 	yych = *++YYCURSOR;
-	if (yych != '"') goto yy18;
+	if (yych <= '/') goto yy18;
+	if (yych >= ':') goto yy18;
+yy57:
 	++YYCURSOR;
-#line 735 "ext/standard/var_unserializer.re"
-	{
-	size_t len, maxlen;
-	zend_string *str;
-
-	len = parse_uiv(start + 2);
-	maxlen = max - YYCURSOR;
-	if (maxlen < len) {
-		*p = start + 2;
-		return 0;
-	}
-
-	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
-		return 0;
-	}
-
-	if (*(YYCURSOR) != '"') {
-		zend_string_free(str);
-		*p = YYCURSOR;
-		return 0;
-	}
-
-	if (*(YYCURSOR + 1) != ';') {
-		efree(str);
-		*p = YYCURSOR + 1;
-		return 0;
-	}
-
-	YYCURSOR += 2;
-	*p = YYCURSOR;
-
-	ZVAL_STR(rval, str);
-	return 1;
-}
-#line 1014 "ext/standard/var_unserializer.c"
-yy46:
-	yych = *++YYCURSOR;
-	if (yych == '+') goto yy47;
+	if (YYLIMIT <= YYCURSOR) YYFILL(1);
+	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy48;
+	if (yych <= '9') goto yy57;
+	if (yych == ';') goto yy79;
 	goto yy18;
-yy47:
+yy59:
 	yych = *++YYCURSOR;
 	if (yych <= '/') goto yy18;
 	if (yych >= ':') goto yy18;
-yy48:
+yy60:
 	++YYCURSOR;
 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
 	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy48;
-	if (yych >= ';') goto yy18;
+	if (yych <= '9') goto yy60;
+	if (yych <= ':') goto yy81;
+	goto yy18;
+yy62:
 	yych = *++YYCURSOR;
-	if (yych != '"') goto yy18;
+	if (yych == '"') goto yy82;
+	goto yy18;
+yy63:
 	++YYCURSOR;
-#line 703 "ext/standard/var_unserializer.re"
+#line 580 "ext/standard/var_unserializer.re"
 	{
-	size_t len, maxlen;
-	char *str;
-
-	len = parse_uiv(start + 2);
-	maxlen = max - YYCURSOR;
-	if (maxlen < len) {
-		*p = start + 2;
-		return 0;
-	}
-
-	str = (char*)YYCURSOR;
+	zend_long id;
 
-	YYCURSOR += len;
+ 	*p = YYCURSOR;
+	if (!var_hash) return 0;
 
-	if (*(YYCURSOR) != '"') {
-		*p = YYCURSOR;
+	id = parse_iv(start + 2) - 1;
+	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
 		return 0;
 	}
 
-	if (*(YYCURSOR + 1) != ';') {
-		*p = YYCURSOR + 1;
+	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
 		return 0;
 	}
 
-	YYCURSOR += 2;
-	*p = YYCURSOR;
+	if (Z_ISREF_P(rval_ref)) {
+		ZVAL_COPY(rval, rval_ref);
+	} else {
+		ZVAL_NEW_REF(rval_ref, rval_ref);
+		ZVAL_COPY(rval, rval_ref);
+	}
 
-	ZVAL_STRINGL(rval, str, len);
 	return 1;
 }
-#line 1067 "ext/standard/var_unserializer.c"
-yy53:
-	yych = *++YYCURSOR;
-	if (yych <= '/') {
-		if (yych <= ',') {
-			if (yych == '+') goto yy57;
-			goto yy18;
-		} else {
-			if (yych <= '-') goto yy55;
-			if (yych <= '.') goto yy60;
-			goto yy18;
-		}
-	} else {
-		if (yych <= 'I') {
-			if (yych <= '9') goto yy58;
-			if (yych <= 'H') goto yy18;
-			goto yy56;
-		} else {
-			if (yych != 'N') goto yy18;
-		}
-	}
+#line 982 "ext/standard/var_unserializer.c"
+yy65:
 	yych = *++YYCURSOR;
-	if (yych == 'A') goto yy76;
+	if (yych == '"') goto yy84;
 	goto yy18;
-yy55:
-	yych = *++YYCURSOR;
-	if (yych <= '/') {
-		if (yych == '.') goto yy60;
-		goto yy18;
-	} else {
-		if (yych <= '9') goto yy58;
-		if (yych != 'I') goto yy18;
-	}
-yy56:
+yy66:
 	yych = *++YYCURSOR;
-	if (yych == 'N') goto yy72;
+	if (yych == '{') goto yy86;
 	goto yy18;
-yy57:
-	yych = *++YYCURSOR;
-	if (yych == '.') goto yy60;
-	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
-yy58:
+yy67:
 	++YYCURSOR;
-	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
-	yych = *YYCURSOR;
-	if (yych <= ':') {
-		if (yych <= '.') {
-			if (yych <= '-') goto yy18;
-			goto yy70;
-		} else {
-			if (yych <= '/') goto yy18;
-			if (yych <= '9') goto yy58;
-			goto yy18;
-		}
-	} else {
-		if (yych <= 'E') {
-			if (yych <= ';') goto yy63;
-			if (yych <= 'D') goto yy18;
-			goto yy65;
-		} else {
-			if (yych == 'e') goto yy65;
-			goto yy18;
-		}
-	}
-yy60:
-	yych = *++YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
-yy61:
+#line 635 "ext/standard/var_unserializer.re"
+	{
+	*p = YYCURSOR;
+	ZVAL_BOOL(rval, parse_iv(start + 2));
+	return 1;
+}
+#line 999 "ext/standard/var_unserializer.c"
+yy69:
 	++YYCURSOR;
 	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
 	yych = *YYCURSOR;
 	if (yych <= ';') {
 		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy61;
+		if (yych <= '9') goto yy69;
 		if (yych <= ':') goto yy18;
 	} else {
 		if (yych <= 'E') {
 			if (yych <= 'D') goto yy18;
-			goto yy65;
+			goto yy73;
 		} else {
-			if (yych == 'e') goto yy65;
+			if (yych == 'e') goto yy73;
 			goto yy18;
 		}
 	}
-yy63:
+yy71:
 	++YYCURSOR;
-#line 694 "ext/standard/var_unserializer.re"
+#line 683 "ext/standard/var_unserializer.re"
 	{
 #if SIZEOF_ZEND_LONG == 4
 use_double:
 #endif
 	*p = YYCURSOR;
 	ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
 	return 1;
 }
-#line 1164 "ext/standard/var_unserializer.c"
-yy65:
+#line 1028 "ext/standard/var_unserializer.c"
+yy73:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
-		if (yych != '+') goto yy18;
+		if (yych == '+') goto yy88;
+		goto yy18;
 	} else {
-		if (yych <= '-') goto yy66;
+		if (yych <= '-') goto yy88;
 		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy67;
+		if (yych <= '9') goto yy89;
 		goto yy18;
 	}
-yy66:
+yy74:
 	yych = *++YYCURSOR;
-	if (yych <= ',') {
-		if (yych == '+') goto yy69;
-		goto yy18;
-	} else {
-		if (yych <= '-') goto yy69;
-		if (yych <= '/') goto yy18;
-		if (yych >= ':') goto yy18;
-	}
-yy67:
-	++YYCURSOR;
-	if (YYLIMIT <= YYCURSOR) YYFILL(1);
-	yych = *YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy67;
-	if (yych == ';') goto yy63;
+	if (yych == 'F') goto yy91;
 	goto yy18;
-yy69:
+yy75:
 	yych = *++YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy67;
+	if (yych == 'N') goto yy91;
 	goto yy18;
-yy70:
+yy76:
 	++YYCURSOR;
-	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
-	yych = *YYCURSOR;
-	if (yych <= ';') {
-		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy70;
-		if (yych <= ':') goto yy18;
-		goto yy63;
-	} else {
-		if (yych <= 'E') {
-			if (yych <= 'D') goto yy18;
-			goto yy65;
+#line 641 "ext/standard/var_unserializer.re"
+	{
+#if SIZEOF_ZEND_LONG == 4
+	int digits = YYCURSOR - start - 3;
+
+	if (start[2] == '-' || start[2] == '+') {
+		digits--;
+	}
+
+	/* Use double for large zend_long values that were serialized on a 64-bit system */
+	if (digits >= MAX_LENGTH_OF_LONG - 1) {
+		if (digits == MAX_LENGTH_OF_LONG - 1) {
+			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
+
+			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
+				goto use_double;
+			}
 		} else {
-			if (yych == 'e') goto yy65;
-			goto yy18;
+			goto use_double;
 		}
 	}
-yy72:
-	yych = *++YYCURSOR;
-	if (yych != 'F') goto yy18;
-yy73:
+#endif
+	*p = YYCURSOR;
+	ZVAL_LONG(rval, parse_iv(start + 2));
+	return 1;
+}
+#line 1076 "ext/standard/var_unserializer.c"
+yy78:
 	yych = *++YYCURSOR;
-	if (yych != ';') goto yy18;
+	if (yych == '"') goto yy92;
+	goto yy18;
+yy79:
 	++YYCURSOR;
-#line 678 "ext/standard/var_unserializer.re"
+#line 605 "ext/standard/var_unserializer.re"
 	{
-	*p = YYCURSOR;
+	zend_long id;
 
-	if (!strncmp((char*)start + 2, "NAN", 3)) {
-		ZVAL_DOUBLE(rval, php_get_nan());
-	} else if (!strncmp((char*)start + 2, "INF", 3)) {
-		ZVAL_DOUBLE(rval, php_get_inf());
-	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
-		ZVAL_DOUBLE(rval, -php_get_inf());
-	} else {
-		ZVAL_NULL(rval);
+ 	*p = YYCURSOR;
+	if (!var_hash) return 0;
+
+	id = parse_iv(start + 2) - 1;
+	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
+		return 0;
+	}
+
+	if (rval_ref == rval) {
+		return 0;
 	}
 
+	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
+		return 0;
+	}
+
+	ZVAL_COPY(rval, rval_ref);
+
 	return 1;
 }
-#line 1239 "ext/standard/var_unserializer.c"
-yy76:
+#line 1107 "ext/standard/var_unserializer.c"
+yy81:
 	yych = *++YYCURSOR;
-	if (yych == 'N') goto yy73;
+	if (yych == '"') goto yy94;
 	goto yy18;
-yy77:
-	yych = *++YYCURSOR;
-	if (yych <= ',') {
-		if (yych != '+') goto yy18;
-	} else {
-		if (yych <= '-') goto yy78;
-		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy79;
-		goto yy18;
-	}
-yy78:
-	yych = *++YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
-yy79:
+yy82:
 	++YYCURSOR;
-	if (YYLIMIT <= YYCURSOR) YYFILL(1);
-	yych = *YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy79;
-	if (yych != ';') goto yy18;
-	++YYCURSOR;
-#line 652 "ext/standard/var_unserializer.re"
+#line 793 "ext/standard/var_unserializer.re"
 	{
-#if SIZEOF_ZEND_LONG == 4
-	int digits = YYCURSOR - start - 3;
+	size_t len, len2, len3, maxlen;
+	zend_long elements;
+	char *str;
+	zend_string *class_name;
+	zend_class_entry *ce;
+	int incomplete_class = 0;
 
-	if (start[2] == '-' || start[2] == '+') {
-		digits--;
+	int custom_object = 0;
+
+	zval user_func;
+	zval retval;
+	zval args[1];
+
+    if (!var_hash) return 0;
+	if (*start == 'C') {
+		custom_object = 1;
 	}
 
-	/* Use double for large zend_long values that were serialized on a 64-bit system */
-	if (digits >= MAX_LENGTH_OF_LONG - 1) {
-		if (digits == MAX_LENGTH_OF_LONG - 1) {
-			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
+	len2 = len = parse_uiv(start + 2);
+	maxlen = max - YYCURSOR;
+	if (maxlen < len || len == 0) {
+		*p = start + 2;
+		return 0;
+	}
 
-			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
-				goto use_double;
+	str = (char*)YYCURSOR;
+
+	YYCURSOR += len;
+
+	if (*(YYCURSOR) != '"') {
+		*p = YYCURSOR;
+		return 0;
+	}
+	if (*(YYCURSOR+1) != ':') {
+		*p = YYCURSOR+1;
+		return 0;
+	}
+
+	len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
+	if (len3 != len)
+	{
+		*p = YYCURSOR + len3 - len;
+		return 0;
+	}
+
+	class_name = zend_string_init(str, len, 0);
+
+	do {
+		if(!unserialize_allowed_class(class_name, classes)) {
+			incomplete_class = 1;
+			ce = PHP_IC_ENTRY;
+			break;
+		}
+
+		/* Try to find class directly */
+		BG(serialize_lock)++;
+		ce = zend_lookup_class(class_name);
+		if (ce) {
+			BG(serialize_lock)--;
+			if (EG(exception)) {
+				zend_string_release(class_name);
+				return 0;
 			}
-		} else {
-			goto use_double;
+			break;
 		}
-	}
-#endif
+		BG(serialize_lock)--;
+
+		if (EG(exception)) {
+			zend_string_release(class_name);
+			return 0;
+		}
+
+		/* Check for unserialize callback */
+		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
+			incomplete_class = 1;
+			ce = PHP_IC_ENTRY;
+			break;
+		}
+
+		/* Call unserialize callback */
+		ZVAL_STRING(&user_func, PG(unserialize_callback_func));
+
+		ZVAL_STR_COPY(&args[0], class_name);
+		BG(serialize_lock)++;
+		if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
+			BG(serialize_lock)--;
+			if (EG(exception)) {
+				zend_string_release(class_name);
+				zval_ptr_dtor(&user_func);
+				zval_ptr_dtor(&args[0]);
+				return 0;
+			}
+			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
+			incomplete_class = 1;
+			ce = PHP_IC_ENTRY;
+			zval_ptr_dtor(&user_func);
+			zval_ptr_dtor(&args[0]);
+			break;
+		}
+		BG(serialize_lock)--;
+		zval_ptr_dtor(&retval);
+		if (EG(exception)) {
+			zend_string_release(class_name);
+			zval_ptr_dtor(&user_func);
+			zval_ptr_dtor(&args[0]);
+			return 0;
+		}
+
+		/* The callback function may have defined the class */
+		BG(serialize_lock)++;
+		if ((ce = zend_lookup_class(class_name)) == NULL) {
+			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
+			incomplete_class = 1;
+			ce = PHP_IC_ENTRY;
+		}
+		BG(serialize_lock)--;
+
+		zval_ptr_dtor(&user_func);
+		zval_ptr_dtor(&args[0]);
+		break;
+	} while (1);
+
 	*p = YYCURSOR;
-	ZVAL_LONG(rval, parse_iv(start + 2));
-	return 1;
+
+	if (custom_object) {
+		int ret;
+
+		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
+
+		if (ret && incomplete_class) {
+			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
+		}
+		zend_string_release(class_name);
+		return ret;
+	}
+
+	elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
+
+	if (elements < 0) {
+	   zend_string_release(class_name);
+	   return 0;
+	}
+
+	if (incomplete_class) {
+		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
+	}
+	zend_string_release(class_name);
+
+	return object_common2(UNSERIALIZE_PASSTHRU, elements);
 }
-#line 1292 "ext/standard/var_unserializer.c"
-yy83:
-	yych = *++YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych >= '2') goto yy18;
-	yych = *++YYCURSOR;
-	if (yych != ';') goto yy18;
+#line 1266 "ext/standard/var_unserializer.c"
+yy84:
 	++YYCURSOR;
-#line 646 "ext/standard/var_unserializer.re"
+#line 724 "ext/standard/var_unserializer.re"
 	{
+	size_t len, maxlen;
+	zend_string *str;
+
+	len = parse_uiv(start + 2);
+	maxlen = max - YYCURSOR;
+	if (maxlen < len) {
+		*p = start + 2;
+		return 0;
+	}
+
+	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
+		return 0;
+	}
+
+	if (*(YYCURSOR) != '"') {
+		zend_string_free(str);
+		*p = YYCURSOR;
+		return 0;
+	}
+
+	if (*(YYCURSOR + 1) != ';') {
+		efree(str);
+		*p = YYCURSOR + 1;
+		return 0;
+	}
+
+	YYCURSOR += 2;
 	*p = YYCURSOR;
-	ZVAL_BOOL(rval, parse_iv(start + 2));
+
+	ZVAL_STR(rval, str);
 	return 1;
 }
-#line 1306 "ext/standard/var_unserializer.c"
-yy87:
+#line 1303 "ext/standard/var_unserializer.c"
+yy86:
 	++YYCURSOR;
-#line 640 "ext/standard/var_unserializer.re"
+#line 758 "ext/standard/var_unserializer.re"
 	{
+	zend_long elements = parse_iv(start + 2);
+	/* use iv() not uiv() in order to check data range */
 	*p = YYCURSOR;
-	ZVAL_NULL(rval);
-	return 1;
+    if (!var_hash) return 0;
+
+	if (elements < 0 || elements >= HT_MAX_SIZE) {
+		return 0;
+	}
+
+	array_init_size(rval, elements);
+	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);
+	}
+
+	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
+		return 0;
+	}
+
+	return finish_nested_data(UNSERIALIZE_PASSTHRU);
 }
-#line 1315 "ext/standard/var_unserializer.c"
-yy89:
+#line 1330 "ext/standard/var_unserializer.c"
+yy88:
 	yych = *++YYCURSOR;
 	if (yych <= ',') {
-		if (yych != '+') goto yy18;
+		if (yych == '+') goto yy96;
+		goto yy18;
 	} else {
-		if (yych <= '-') goto yy90;
+		if (yych <= '-') goto yy96;
 		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy91;
-		goto yy18;
+		if (yych >= ':') goto yy18;
 	}
-yy90:
-	yych = *++YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
-yy91:
+yy89:
 	++YYCURSOR;
 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
 	yych = *YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy91;
-	if (yych != ';') goto yy18;
+	if (yych <= '9') goto yy89;
+	if (yych == ';') goto yy71;
+	goto yy18;
+yy91:
+	yych = *++YYCURSOR;
+	if (yych == ';') goto yy97;
+	goto yy18;
+yy92:
 	++YYCURSOR;
-#line 615 "ext/standard/var_unserializer.re"
+#line 782 "ext/standard/var_unserializer.re"
 	{
-	zend_long id;
+	zend_long elements;
+    if (!var_hash) return 0;
 
- 	*p = YYCURSOR;
-	if (!var_hash) return 0;
+	elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
+	if (elements < 0 || elements >= HT_MAX_SIZE) {
+		return 0;
+	}
+	return object_common2(UNSERIALIZE_PASSTHRU, elements);
+}
+#line 1366 "ext/standard/var_unserializer.c"
+yy94:
+	++YYCURSOR;
+#line 692 "ext/standard/var_unserializer.re"
+	{
+	size_t len, maxlen;
+	char *str;
 
-	id = parse_iv(start + 2) - 1;
-	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
+	len = parse_uiv(start + 2);
+	maxlen = max - YYCURSOR;
+	if (maxlen < len) {
+		*p = start + 2;
 		return 0;
 	}
 
-	if (rval_ref == rval) {
+	str = (char*)YYCURSOR;
+
+	YYCURSOR += len;
+
+	if (*(YYCURSOR) != '"') {
+		*p = YYCURSOR;
 		return 0;
 	}
 
-	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
-		ZVAL_UNDEF(rval);
-		return 1;
+	if (*(YYCURSOR + 1) != ';') {
+		*p = YYCURSOR + 1;
+		return 0;
 	}
 
-	ZVAL_COPY(rval, rval_ref);
+	YYCURSOR += 2;
+	*p = YYCURSOR;
 
+	ZVAL_STRINGL(rval, str, len);
 	return 1;
 }
-#line 1363 "ext/standard/var_unserializer.c"
-yy95:
-	yych = *++YYCURSOR;
-	if (yych <= ',') {
-		if (yych != '+') goto yy18;
-	} else {
-		if (yych <= '-') goto yy96;
-		if (yych <= '/') goto yy18;
-		if (yych <= '9') goto yy97;
-		goto yy18;
-	}
+#line 1401 "ext/standard/var_unserializer.c"
 yy96:
 	yych = *++YYCURSOR;
 	if (yych <= '/') goto yy18;
-	if (yych >= ':') goto yy18;
+	if (yych <= '9') goto yy89;
+	goto yy18;
 yy97:
 	++YYCURSOR;
-	if (YYLIMIT <= YYCURSOR) YYFILL(1);
-	yych = *YYCURSOR;
-	if (yych <= '/') goto yy18;
-	if (yych <= '9') goto yy97;
-	if (yych != ';') goto yy18;
-	++YYCURSOR;
-#line 589 "ext/standard/var_unserializer.re"
+#line 667 "ext/standard/var_unserializer.re"
 	{
-	zend_long id;
-
- 	*p = YYCURSOR;
-	if (!var_hash) return 0;
-
-	id = parse_iv(start + 2) - 1;
-	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
-		return 0;
-	}
+	*p = YYCURSOR;
 
-	zval_ptr_dtor(rval);
-	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
-		ZVAL_UNDEF(rval);
-		return 1;
-	}
-	if (Z_ISREF_P(rval_ref)) {
-		ZVAL_COPY(rval, rval_ref);
+	if (!strncmp((char*)start + 2, "NAN", 3)) {
+		ZVAL_DOUBLE(rval, php_get_nan());
+	} else if (!strncmp((char*)start + 2, "INF", 3)) {
+		ZVAL_DOUBLE(rval, php_get_inf());
+	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
+		ZVAL_DOUBLE(rval, -php_get_inf());
 	} else {
-		ZVAL_NEW_REF(rval_ref, rval_ref);
-		ZVAL_COPY(rval, rval_ref);
+		ZVAL_NULL(rval);
 	}
 
 	return 1;
 }
-#line 1412 "ext/standard/var_unserializer.c"
+#line 1425 "ext/standard/var_unserializer.c"
 }
-#line 964 "ext/standard/var_unserializer.re"
+#line 953 "ext/standard/var_unserializer.re"
 
 
 	return 0;
 }
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index f3d17b63f62..c5052f0457e 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -407,20 +407,11 @@ string_key:
 		if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) {
 			zval_dtor(&key);
 			return 0;
 		}
 
-		if (UNEXPECTED(Z_ISUNDEF_P(data))) {
-			if (Z_TYPE(key) == IS_LONG) {
-				zend_hash_index_del(ht, Z_LVAL(key));
-			} else {
-				zend_hash_del_ind(ht, Z_STR(key));
-			}
-		} else {
-			var_push_dtor(var_hash, data);
-		}
-
+		var_push_dtor(var_hash, data);
 		zval_dtor(&key);
 
 		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
 			(*p)--;
 			return 0;
@@ -595,15 +586,14 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
 	id = parse_iv(start + 2) - 1;
 	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
 		return 0;
 	}
 
-	zval_ptr_dtor(rval);
 	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
-		ZVAL_UNDEF(rval);
-		return 1;
+		return 0;
 	}
+
 	if (Z_ISREF_P(rval_ref)) {
 		ZVAL_COPY(rval, rval_ref);
 	} else {
 		ZVAL_NEW_REF(rval_ref, rval_ref);
 		ZVAL_COPY(rval, rval_ref);
@@ -626,12 +616,11 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
 	if (rval_ref == rval) {
 		return 0;
 	}
 
 	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
-		ZVAL_UNDEF(rval);
-		return 1;
+		return 0;
 	}
 
 	ZVAL_COPY(rval, rval_ref);
 
 	return 1;

Vulnerable code: /* Generated by re2c 0.15.3 */
		if (UNEXPECTED(Z_ISUNDEF_P(data))) {
			if (Z_TYPE(key) == IS_LONG) {
				zend_hash_index_del(ht, Z_LVAL(key));
			} else {
				zend_hash_del_ind(ht, Z_STR(key));
			}
		} else {
			var_push_dtor(var_hash, data);
		}

#line 585 "ext/standard/var_unserializer.c"
	case 'O':	goto yy13;
	case 'R':	goto yy2;
	case 'S':	goto yy10;
	case 'a':	goto yy11;
	case 'b':	goto yy6;
	case 'd':	goto yy8;
	case 'i':	goto yy7;
	case 'r':	goto yy4;
	case 's':	goto yy9;
	case '}':	goto yy14;
	default:	goto yy16;
	yych = *(YYMARKER = ++YYCURSOR);
	if (yych == ':') goto yy95;
#line 962 "ext/standard/var_unserializer.re"
#line 646 "ext/standard/var_unserializer.c"
	if (yych == ':') goto yy89;
	if (yych == ';') goto yy87;
	if (yych == ':') goto yy83;
	if (yych == ':') goto yy77;
	if (yych == ':') goto yy53;
	if (yych == ':') goto yy46;
	if (yych == ':') goto yy39;
	if (yych == ':') goto yy32;
	if (yych == ':') goto yy25;
	if (yych == ':') goto yy17;
#line 956 "ext/standard/var_unserializer.re"
#line 695 "ext/standard/var_unserializer.c"
yy16:
	yych = *++YYCURSOR;
	goto yy3;
		goto yy20;
	if (yych == '+') goto yy19;
	if (yybm[0+yych] & 128) {
		goto yy20;
yy20:
	++YYCURSOR;
	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
	yych = *YYCURSOR;
	if (yybm[0+yych] & 128) {
		goto yy20;
	}
	if (yych >= ';') goto yy18;
	if (yych != '"') goto yy18;
	++YYCURSOR;
#line 804 "ext/standard/var_unserializer.re"
	{
	size_t len, len2, len3, maxlen;
	zend_long elements;
	char *str;
	zend_string *class_name;
	zend_class_entry *ce;
	int incomplete_class = 0;

	int custom_object = 0;

	zval user_func;
	zval retval;
	zval args[1];

    if (!var_hash) return 0;
	if (*start == 'C') {
		custom_object = 1;
	}

	len2 = len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len || len == 0) {
		*p = start + 2;
		return 0;
	}

	str = (char*)YYCURSOR;

	YYCURSOR += len;

	if (*(YYCURSOR) != '"') {
		*p = YYCURSOR;
		return 0;
	}
	if (*(YYCURSOR+1) != ':') {
		*p = YYCURSOR+1;
		return 0;
	}

	len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
	if (len3 != len)
	{
		*p = YYCURSOR + len3 - len;
		return 0;
	}

	class_name = zend_string_init(str, len, 0);

	do {
		if(!unserialize_allowed_class(class_name, classes)) {
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			break;
		}

		/* Try to find class directly */
		BG(serialize_lock)++;
		ce = zend_lookup_class(class_name);
		if (ce) {
			BG(serialize_lock)--;
			if (EG(exception)) {
				zend_string_release(class_name);
				return 0;
			}
			break;
		}
		BG(serialize_lock)--;

		if (EG(exception)) {
			zend_string_release(class_name);
			return 0;
		}

		/* Check for unserialize callback */
		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			break;
		}

		/* Call unserialize callback */
		ZVAL_STRING(&user_func, PG(unserialize_callback_func));

		ZVAL_STR_COPY(&args[0], class_name);
		BG(serialize_lock)++;
		if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
			BG(serialize_lock)--;
			if (EG(exception)) {
				zend_string_release(class_name);
				zval_ptr_dtor(&user_func);
				zval_ptr_dtor(&args[0]);
				return 0;
			}
			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			zval_ptr_dtor(&user_func);
			zval_ptr_dtor(&args[0]);
			break;
		}
		BG(serialize_lock)--;
		zval_ptr_dtor(&retval);
		if (EG(exception)) {
			zend_string_release(class_name);
			zval_ptr_dtor(&user_func);
			zval_ptr_dtor(&args[0]);
			return 0;
		}

		/* The callback function may have defined the class */
		BG(serialize_lock)++;
		if ((ce = zend_lookup_class(class_name)) == NULL) {
			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
		BG(serialize_lock)--;

		zval_ptr_dtor(&user_func);
		zval_ptr_dtor(&args[0]);
		break;
	} while (1);

	*p = YYCURSOR;

	if (custom_object) {
		int ret;

		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);

		if (ret && incomplete_class) {
			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
		zend_string_release(class_name);
		return ret;

	elements = object_common1(UNSERIALIZE_PASSTHRU, ce);

	if (elements < 0) {
	   zend_string_release(class_name);
	   return 0;

	if (incomplete_class) {
		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
	zend_string_release(class_name);

	return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
#line 878 "ext/standard/var_unserializer.c"
yy25:
		if (yych != '+') goto yy18;
		if (yych <= '-') goto yy26;
		if (yych <= '9') goto yy27;
yy26:
	if (yych >= ':') goto yy18;
yy27:
	if (yych <= '9') goto yy27;
	if (yych >= ';') goto yy18;
	if (yych != '"') goto yy18;
#line 793 "ext/standard/var_unserializer.re"
	{
	zend_long elements;
    if (!var_hash) return 0;

	elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
	if (elements < 0 || elements >= HT_MAX_SIZE) {
		return 0;
	}
	return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
#line 914 "ext/standard/var_unserializer.c"
yy32:
	yych = *++YYCURSOR;
	if (yych == '+') goto yy33;
yy33:
yy34:
	if (yych <= '9') goto yy34;
	if (yych >= ';') goto yy18;
	if (yych != '{') goto yy18;
#line 769 "ext/standard/var_unserializer.re"
	{
	zend_long elements = parse_iv(start + 2);
	/* use iv() not uiv() in order to check data range */
	*p = YYCURSOR;
    if (!var_hash) return 0;

	if (elements < 0 || elements >= HT_MAX_SIZE) {
		return 0;
	}

	array_init_size(rval, elements);
	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);

	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
		return 0;

	return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
#line 959 "ext/standard/var_unserializer.c"
yy39:
	if (yych == '+') goto yy40;
	if (yych <= '9') goto yy41;
yy40:
yy41:
	if (yych <= '9') goto yy41;
	if (yych >= ';') goto yy18;
	if (yych != '"') goto yy18;
#line 735 "ext/standard/var_unserializer.re"
	{
	size_t len, maxlen;
	zend_string *str;

	len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len) {
		*p = start + 2;
		return 0;
	}

	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
		return 0;
	}

	if (*(YYCURSOR) != '"') {
		zend_string_free(str);
		*p = YYCURSOR;
		return 0;
	}

	if (*(YYCURSOR + 1) != ';') {
		efree(str);
		*p = YYCURSOR + 1;
		return 0;
	}

	YYCURSOR += 2;
	*p = YYCURSOR;

	ZVAL_STR(rval, str);
	return 1;
}
#line 1014 "ext/standard/var_unserializer.c"
yy46:
	yych = *++YYCURSOR;
	if (yych == '+') goto yy47;
	if (yych <= '9') goto yy48;
yy47:
yy48:
	if (yych <= '9') goto yy48;
	if (yych >= ';') goto yy18;
	if (yych != '"') goto yy18;
#line 703 "ext/standard/var_unserializer.re"
	size_t len, maxlen;
	char *str;

	len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len) {
		*p = start + 2;
		return 0;
	}

	str = (char*)YYCURSOR;
	YYCURSOR += len;
	if (*(YYCURSOR) != '"') {
		*p = YYCURSOR;
	if (*(YYCURSOR + 1) != ';') {
		*p = YYCURSOR + 1;
	YYCURSOR += 2;
	*p = YYCURSOR;
	ZVAL_STRINGL(rval, str, len);
#line 1067 "ext/standard/var_unserializer.c"
yy53:
	yych = *++YYCURSOR;
	if (yych <= '/') {
		if (yych <= ',') {
			if (yych == '+') goto yy57;
			goto yy18;
		} else {
			if (yych <= '-') goto yy55;
			if (yych <= '.') goto yy60;
			goto yy18;
		}
	} else {
		if (yych <= 'I') {
			if (yych <= '9') goto yy58;
			if (yych <= 'H') goto yy18;
			goto yy56;
		} else {
			if (yych != 'N') goto yy18;
		}
	}
	if (yych == 'A') goto yy76;
yy55:
	yych = *++YYCURSOR;
	if (yych <= '/') {
		if (yych == '.') goto yy60;
		goto yy18;
	} else {
		if (yych <= '9') goto yy58;
		if (yych != 'I') goto yy18;
	}
yy56:
	if (yych == 'N') goto yy72;
yy57:
	yych = *++YYCURSOR;
	if (yych == '.') goto yy60;
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy58:
	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
	yych = *YYCURSOR;
	if (yych <= ':') {
		if (yych <= '.') {
			if (yych <= '-') goto yy18;
			goto yy70;
		} else {
			if (yych <= '/') goto yy18;
			if (yych <= '9') goto yy58;
			goto yy18;
		}
	} else {
		if (yych <= 'E') {
			if (yych <= ';') goto yy63;
			if (yych <= 'D') goto yy18;
			goto yy65;
		} else {
			if (yych == 'e') goto yy65;
			goto yy18;
		}
	}
yy60:
	yych = *++YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy61:
		if (yych <= '9') goto yy61;
			goto yy65;
			if (yych == 'e') goto yy65;
yy63:
#line 694 "ext/standard/var_unserializer.re"
#line 1164 "ext/standard/var_unserializer.c"
yy65:
		if (yych != '+') goto yy18;
		if (yych <= '-') goto yy66;
		if (yych <= '9') goto yy67;
yy66:
	if (yych <= ',') {
		if (yych == '+') goto yy69;
		goto yy18;
	} else {
		if (yych <= '-') goto yy69;
		if (yych <= '/') goto yy18;
		if (yych >= ':') goto yy18;
	}
yy67:
	++YYCURSOR;
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy67;
	if (yych == ';') goto yy63;
yy69:
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy67;
yy70:
	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
	yych = *YYCURSOR;
	if (yych <= ';') {
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy70;
		if (yych <= ':') goto yy18;
		goto yy63;
	} else {
		if (yych <= 'E') {
			if (yych <= 'D') goto yy18;
			goto yy65;
			if (yych == 'e') goto yy65;
			goto yy18;
yy72:
	yych = *++YYCURSOR;
	if (yych != 'F') goto yy18;
yy73:
	if (yych != ';') goto yy18;
#line 678 "ext/standard/var_unserializer.re"
	*p = YYCURSOR;
	if (!strncmp((char*)start + 2, "NAN", 3)) {
		ZVAL_DOUBLE(rval, php_get_nan());
	} else if (!strncmp((char*)start + 2, "INF", 3)) {
		ZVAL_DOUBLE(rval, php_get_inf());
	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
		ZVAL_DOUBLE(rval, -php_get_inf());
	} else {
		ZVAL_NULL(rval);
#line 1239 "ext/standard/var_unserializer.c"
yy76:
	if (yych == 'N') goto yy73;
yy77:
	yych = *++YYCURSOR;
	if (yych <= ',') {
		if (yych != '+') goto yy18;
	} else {
		if (yych <= '-') goto yy78;
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy79;
		goto yy18;
	}
yy78:
	yych = *++YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy79:
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy79;
	if (yych != ';') goto yy18;
	++YYCURSOR;
#line 652 "ext/standard/var_unserializer.re"
#if SIZEOF_ZEND_LONG == 4
	int digits = YYCURSOR - start - 3;
	if (start[2] == '-' || start[2] == '+') {
		digits--;
	/* Use double for large zend_long values that were serialized on a 64-bit system */
	if (digits >= MAX_LENGTH_OF_LONG - 1) {
		if (digits == MAX_LENGTH_OF_LONG - 1) {
			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
				goto use_double;
		} else {
			goto use_double;
	}
#endif
	ZVAL_LONG(rval, parse_iv(start + 2));
	return 1;
#line 1292 "ext/standard/var_unserializer.c"
yy83:
	yych = *++YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych >= '2') goto yy18;
	yych = *++YYCURSOR;
	if (yych != ';') goto yy18;
#line 646 "ext/standard/var_unserializer.re"
	ZVAL_BOOL(rval, parse_iv(start + 2));
#line 1306 "ext/standard/var_unserializer.c"
yy87:
#line 640 "ext/standard/var_unserializer.re"
	ZVAL_NULL(rval);
	return 1;
#line 1315 "ext/standard/var_unserializer.c"
yy89:
		if (yych != '+') goto yy18;
		if (yych <= '-') goto yy90;
		if (yych <= '9') goto yy91;
		goto yy18;
yy90:
	yych = *++YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy91:
	if (yych <= '9') goto yy91;
	if (yych != ';') goto yy18;
#line 615 "ext/standard/var_unserializer.re"
	zend_long id;
 	*p = YYCURSOR;
	if (!var_hash) return 0;
	id = parse_iv(start + 2) - 1;
	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
	if (rval_ref == rval) {
	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
		ZVAL_UNDEF(rval);
		return 1;
	ZVAL_COPY(rval, rval_ref);
#line 1363 "ext/standard/var_unserializer.c"
yy95:
	yych = *++YYCURSOR;
	if (yych <= ',') {
		if (yych != '+') goto yy18;
	} else {
		if (yych <= '-') goto yy96;
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy97;
		goto yy18;
	}
	if (yych >= ':') goto yy18;
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy97;
	if (yych != ';') goto yy18;
	++YYCURSOR;
#line 589 "ext/standard/var_unserializer.re"
	zend_long id;

 	*p = YYCURSOR;
	if (!var_hash) return 0;

	id = parse_iv(start + 2) - 1;
	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
		return 0;
	}
	zval_ptr_dtor(rval);
	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
		ZVAL_UNDEF(rval);
		return 1;
	}
	if (Z_ISREF_P(rval_ref)) {
		ZVAL_COPY(rval, rval_ref);
		ZVAL_NEW_REF(rval_ref, rval_ref);
		ZVAL_COPY(rval, rval_ref);
#line 1412 "ext/standard/var_unserializer.c"
#line 964 "ext/standard/var_unserializer.re"
		if (UNEXPECTED(Z_ISUNDEF_P(data))) {
			if (Z_TYPE(key) == IS_LONG) {
				zend_hash_index_del(ht, Z_LVAL(key));
			} else {
				zend_hash_del_ind(ht, Z_STR(key));
			}
		} else {
			var_push_dtor(var_hash, data);
		}

	zval_ptr_dtor(rval);
		ZVAL_UNDEF(rval);
		return 1;
		ZVAL_UNDEF(rval);
		return 1;
Fixed code: - Standard:
  . Fixed bug #74103 (heap-use-after-free when unserializing invalid array
    size). (Nikita)
  . Fixed bug #75054 (A Denial of Service Vulnerability was found when
    performing deserialization). (Nikita)

--TEST--
Bug #74103: heap-use-after-free when unserializing invalid array size
--FILE--
<?php
var_dump(unserialize('a:7:{i:0;i:04;s:1:"a";i:2;i:00009617006;i:4;s:1:"a";i:4;s:1:"a";R:5;s:1:"7";R:3;s:1:"a";R:5;;s:18;}}'));
?>
--EXPECTF--
Notice: unserialize(): Error at offset 68 of 100 bytes in %s on line %d
bool(false)
--TEST--
Bug #75054: A Denial of Service Vulnerability was found when performing deserialization
--FILE--
<?php
$poc = 'a:9:{i:0;s:4:"0000";i:0;s:4:"0000";i:0;R:2;s:4:"5003";R:2;s:4:"0000";R:2;s:4:"0000";R:2;s:4:"';
$poc .= "\x06";
$poc .= '000";R:2;s:4:"0000";d:0;s:4:"0000";a:9:{s:4:"0000";';
var_dump(unserialize($poc));
?>
--EXPECTF--
Notice: unserialize(): Error at offset 43 of 145 bytes in %s on line %d
bool(false)
/* Generated by re2c 0.16 */
		var_push_dtor(var_hash, data);
#line 576 "ext/standard/var_unserializer.c"
	case 'O':	goto yy4;
	case 'R':	goto yy6;
	case 'S':	goto yy7;
	case 'a':	goto yy8;
	case 'b':	goto yy9;
	case 'd':	goto yy10;
	case 'i':	goto yy11;
	case 'r':	goto yy13;
	case 's':	goto yy14;
	case '}':	goto yy15;
	default:	goto yy2;
	++YYCURSOR;
#line 951 "ext/standard/var_unserializer.re"
#line 636 "ext/standard/var_unserializer.c"
	if (yych == ':') goto yy17;
	if (yych == ';') goto yy19;
	if (yych == ':') goto yy21;
	if (yych == ':') goto yy22;
	if (yych == ':') goto yy23;
	if (yych == ':') goto yy24;
	if (yych == ':') goto yy25;
	if (yych == ':') goto yy26;
	if (yych == ':') goto yy27;
	if (yych == ':') goto yy28;
	yych = *(YYMARKER = ++YYCURSOR);
	if (yych == ':') goto yy29;
	goto yy3;
yy15:
#line 945 "ext/standard/var_unserializer.re"
#line 689 "ext/standard/var_unserializer.c"
		goto yy31;
	if (yych == '+') goto yy30;
	++YYCURSOR;
#line 629 "ext/standard/var_unserializer.re"
	{
	*p = YYCURSOR;
	ZVAL_NULL(rval);
	return 1;
}
#line 707 "ext/standard/var_unserializer.c"
yy21:
	if (yych <= ',') {
		if (yych == '+') goto yy33;
		goto yy18;
	} else {
		if (yych <= '-') goto yy33;
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy34;
		goto yy18;
yy22:
	yych = *++YYCURSOR;
	if (yych == '+') goto yy36;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy37;
yy23:
	yych = *++YYCURSOR;
	if (yych == '+') goto yy39;
	if (yych <= '9') goto yy40;
	goto yy18;
yy24:
	if (yych <= '/') goto yy18;
	if (yych <= '1') goto yy42;
	goto yy18;
yy25:
	yych = *++YYCURSOR;
	if (yych <= '/') {
		if (yych <= ',') {
			if (yych == '+') goto yy43;
			goto yy18;
		} else {
			if (yych <= '-') goto yy44;
			if (yych <= '.') goto yy45;
			goto yy18;
	} else {
		if (yych <= 'I') {
			if (yych <= '9') goto yy46;
			if (yych <= 'H') goto yy18;
			goto yy48;
		} else {
			if (yych == 'N') goto yy49;
			goto yy18;
yy26:
	yych = *++YYCURSOR;
	if (yych <= ',') {
		if (yych == '+') goto yy50;
		goto yy18;
	} else {
		if (yych <= '-') goto yy50;
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy51;
		goto yy18;
yy27:
	yych = *++YYCURSOR;
	if (yych <= ',') {
		if (yych == '+') goto yy53;
		goto yy18;
	} else {
		if (yych <= '-') goto yy53;
		if (yych <= '/') goto yy18;
		if (yych <= '9') goto yy54;
		goto yy18;
yy28:
		if (yych == '+') goto yy56;
		goto yy18;
		if (yych <= '-') goto yy56;
		if (yych <= '9') goto yy57;
yy29:
	if (yych == '+') goto yy59;
	if (yych <= '9') goto yy60;
	goto yy18;
yy30:
	yych = *++YYCURSOR;
	if (yybm[0+yych] & 128) {
		goto yy31;
	}
	goto yy18;
yy31:
	if (yybm[0+yych] & 128) {
		goto yy31;
	}
	if (yych <= ':') goto yy62;
	goto yy18;
yy33:
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy34:
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych == ';') goto yy63;
yy36:
yy37:
	if (yych <= '9') goto yy37;
	if (yych <= ':') goto yy65;
	goto yy18;
yy39:
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy40:
	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
	yych = *YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy40;
	if (yych <= ':') goto yy66;
	goto yy18;
yy42:
	yych = *++YYCURSOR;
	if (yych == ';') goto yy67;
	goto yy18;
yy43:
	yych = *++YYCURSOR;
	if (yych == '.') goto yy45;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy46;
	goto yy18;
yy44:
	yych = *++YYCURSOR;
	if (yych <= '/') {
		if (yych != '.') goto yy18;
	} else {
		if (yych <= '9') goto yy46;
		if (yych == 'I') goto yy48;
		goto yy18;
yy45:
	yych = *++YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy69;
	goto yy18;
yy46:
	++YYCURSOR;
	if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
	yych = *YYCURSOR;
	if (yych <= ':') {
		if (yych <= '.') {
			if (yych <= '-') goto yy18;
			goto yy69;
		} else {
			if (yych <= '/') goto yy18;
			if (yych <= '9') goto yy46;
			goto yy18;
		}
	} else {
		if (yych <= 'E') {
			if (yych <= ';') goto yy71;
			if (yych <= 'D') goto yy18;
			goto yy73;
		} else {
			if (yych == 'e') goto yy73;
			goto yy18;
		}
yy48:
	yych = *++YYCURSOR;
	if (yych == 'N') goto yy74;
	goto yy18;
yy49:
	yych = *++YYCURSOR;
	if (yych == 'A') goto yy75;
	goto yy18;
yy50:
	if (yych >= ':') goto yy18;
yy51:
	++YYCURSOR;
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych <= '/') goto yy18;
	if (yych <= '9') goto yy51;
	if (yych == ';') goto yy76;
yy53:
yy54:
	if (yych <= '9') goto yy54;
	if (yych <= ':') goto yy78;
	goto yy18;
yy56:
	if (yych <= '/') goto yy18;
	if (yych >= ':') goto yy18;
yy57:
	if (YYLIMIT <= YYCURSOR) YYFILL(1);
	yych = *YYCURSOR;
	if (yych <= '9') goto yy57;
	if (yych == ';') goto yy79;
yy59:
yy60:
	if (yych <= '9') goto yy60;
	if (yych <= ':') goto yy81;
	goto yy18;
yy62:
	if (yych == '"') goto yy82;
	goto yy18;
yy63:
#line 580 "ext/standard/var_unserializer.re"
	zend_long id;
 	*p = YYCURSOR;
	if (!var_hash) return 0;
	id = parse_iv(start + 2) - 1;
	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
	if (Z_ISREF_P(rval_ref)) {
		ZVAL_COPY(rval, rval_ref);
	} else {
		ZVAL_NEW_REF(rval_ref, rval_ref);
		ZVAL_COPY(rval, rval_ref);
	}
#line 982 "ext/standard/var_unserializer.c"
yy65:
	if (yych == '"') goto yy84;
yy66:
	if (yych == '{') goto yy86;
yy67:
#line 635 "ext/standard/var_unserializer.re"
	{
	*p = YYCURSOR;
	ZVAL_BOOL(rval, parse_iv(start + 2));
	return 1;
}
#line 999 "ext/standard/var_unserializer.c"
yy69:
		if (yych <= '9') goto yy69;
			goto yy73;
			if (yych == 'e') goto yy73;
yy71:
#line 683 "ext/standard/var_unserializer.re"
#line 1028 "ext/standard/var_unserializer.c"
yy73:
		if (yych == '+') goto yy88;
		goto yy18;
		if (yych <= '-') goto yy88;
		if (yych <= '9') goto yy89;
yy74:
	if (yych == 'F') goto yy91;
yy75:
	if (yych == 'N') goto yy91;
yy76:
#line 641 "ext/standard/var_unserializer.re"
	{
#if SIZEOF_ZEND_LONG == 4
	int digits = YYCURSOR - start - 3;

	if (start[2] == '-' || start[2] == '+') {
		digits--;
	}

	/* Use double for large zend_long values that were serialized on a 64-bit system */
	if (digits >= MAX_LENGTH_OF_LONG - 1) {
		if (digits == MAX_LENGTH_OF_LONG - 1) {
			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);

			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
				goto use_double;
			}
			goto use_double;
#endif
	*p = YYCURSOR;
	ZVAL_LONG(rval, parse_iv(start + 2));
	return 1;
}
#line 1076 "ext/standard/var_unserializer.c"
yy78:
	if (yych == '"') goto yy92;
	goto yy18;
yy79:
#line 605 "ext/standard/var_unserializer.re"
	zend_long id;
 	*p = YYCURSOR;
	if (!var_hash) return 0;

	id = parse_iv(start + 2) - 1;
	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
		return 0;
	}

	if (rval_ref == rval) {
		return 0;
	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
		return 0;
	}

	ZVAL_COPY(rval, rval_ref);

#line 1107 "ext/standard/var_unserializer.c"
yy81:
	if (yych == '"') goto yy94;
yy82:
#line 793 "ext/standard/var_unserializer.re"
	size_t len, len2, len3, maxlen;
	zend_long elements;
	char *str;
	zend_string *class_name;
	zend_class_entry *ce;
	int incomplete_class = 0;
	int custom_object = 0;

	zval user_func;
	zval retval;
	zval args[1];

    if (!var_hash) return 0;
	if (*start == 'C') {
		custom_object = 1;
	len2 = len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len || len == 0) {
		*p = start + 2;
		return 0;
	}
	str = (char*)YYCURSOR;

	YYCURSOR += len;

	if (*(YYCURSOR) != '"') {
		*p = YYCURSOR;
		return 0;
	}
	if (*(YYCURSOR+1) != ':') {
		*p = YYCURSOR+1;
		return 0;
	}

	len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
	if (len3 != len)
	{
		*p = YYCURSOR + len3 - len;
		return 0;
	}

	class_name = zend_string_init(str, len, 0);

	do {
		if(!unserialize_allowed_class(class_name, classes)) {
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			break;
		}

		/* Try to find class directly */
		BG(serialize_lock)++;
		ce = zend_lookup_class(class_name);
		if (ce) {
			BG(serialize_lock)--;
			if (EG(exception)) {
				zend_string_release(class_name);
				return 0;
			break;
		BG(serialize_lock)--;

		if (EG(exception)) {
			zend_string_release(class_name);
			return 0;
		}

		/* Check for unserialize callback */
		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			break;
		}

		/* Call unserialize callback */
		ZVAL_STRING(&user_func, PG(unserialize_callback_func));

		ZVAL_STR_COPY(&args[0], class_name);
		BG(serialize_lock)++;
		if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
			BG(serialize_lock)--;
			if (EG(exception)) {
				zend_string_release(class_name);
				zval_ptr_dtor(&user_func);
				zval_ptr_dtor(&args[0]);
				return 0;
			}
			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
			zval_ptr_dtor(&user_func);
			zval_ptr_dtor(&args[0]);
			break;
		}
		BG(serialize_lock)--;
		zval_ptr_dtor(&retval);
		if (EG(exception)) {
			zend_string_release(class_name);
			zval_ptr_dtor(&user_func);
			zval_ptr_dtor(&args[0]);
			return 0;
		}

		/* The callback function may have defined the class */
		BG(serialize_lock)++;
		if ((ce = zend_lookup_class(class_name)) == NULL) {
			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
			incomplete_class = 1;
			ce = PHP_IC_ENTRY;
		}
		BG(serialize_lock)--;

		zval_ptr_dtor(&user_func);
		zval_ptr_dtor(&args[0]);
		break;
	} while (1);


	if (custom_object) {
		int ret;

		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);

		if (ret && incomplete_class) {
			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
		}
		zend_string_release(class_name);
		return ret;
	}

	elements = object_common1(UNSERIALIZE_PASSTHRU, ce);

	if (elements < 0) {
	   zend_string_release(class_name);
	   return 0;
	}

	if (incomplete_class) {
		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
	}
	zend_string_release(class_name);

	return object_common2(UNSERIALIZE_PASSTHRU, elements);
#line 1266 "ext/standard/var_unserializer.c"
yy84:
#line 724 "ext/standard/var_unserializer.re"
	size_t len, maxlen;
	zend_string *str;

	len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len) {
		*p = start + 2;
		return 0;
	}

	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
		return 0;
	}

	if (*(YYCURSOR) != '"') {
		zend_string_free(str);
		*p = YYCURSOR;
		return 0;
	}

	if (*(YYCURSOR + 1) != ';') {
		efree(str);
		*p = YYCURSOR + 1;
		return 0;
	}

	YYCURSOR += 2;

	ZVAL_STR(rval, str);
#line 1303 "ext/standard/var_unserializer.c"
yy86:
#line 758 "ext/standard/var_unserializer.re"
	zend_long elements = parse_iv(start + 2);
	/* use iv() not uiv() in order to check data range */
    if (!var_hash) return 0;

	if (elements < 0 || elements >= HT_MAX_SIZE) {
		return 0;
	}

	array_init_size(rval, elements);
	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);
	}

	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
		return 0;
	}

	return finish_nested_data(UNSERIALIZE_PASSTHRU);
#line 1330 "ext/standard/var_unserializer.c"
yy88:
		if (yych == '+') goto yy96;
		goto yy18;
		if (yych <= '-') goto yy96;
		if (yych >= ':') goto yy18;
yy89:
	if (yych <= '9') goto yy89;
	if (yych == ';') goto yy71;
	goto yy18;
yy91:
	yych = *++YYCURSOR;
	if (yych == ';') goto yy97;
	goto yy18;
yy92:
#line 782 "ext/standard/var_unserializer.re"
	zend_long elements;
    if (!var_hash) return 0;
	elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
	if (elements < 0 || elements >= HT_MAX_SIZE) {
		return 0;
	}
	return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
#line 1366 "ext/standard/var_unserializer.c"
yy94:
	++YYCURSOR;
#line 692 "ext/standard/var_unserializer.re"
	{
	size_t len, maxlen;
	char *str;
	len = parse_uiv(start + 2);
	maxlen = max - YYCURSOR;
	if (maxlen < len) {
		*p = start + 2;
	str = (char*)YYCURSOR;

	YYCURSOR += len;

	if (*(YYCURSOR) != '"') {
		*p = YYCURSOR;
	if (*(YYCURSOR + 1) != ';') {
		*p = YYCURSOR + 1;
		return 0;
	YYCURSOR += 2;
	*p = YYCURSOR;
	ZVAL_STRINGL(rval, str, len);
#line 1401 "ext/standard/var_unserializer.c"
	if (yych <= '9') goto yy89;
	goto yy18;
#line 667 "ext/standard/var_unserializer.re"
	*p = YYCURSOR;
	if (!strncmp((char*)start + 2, "NAN", 3)) {
		ZVAL_DOUBLE(rval, php_get_nan());
	} else if (!strncmp((char*)start + 2, "INF", 3)) {
		ZVAL_DOUBLE(rval, php_get_inf());
	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
		ZVAL_DOUBLE(rval, -php_get_inf());
		ZVAL_NULL(rval);
#line 1425 "ext/standard/var_unserializer.c"
#line 953 "ext/standard/var_unserializer.re"
		var_push_dtor(var_hash, data);
		return 0;

		return 0;