CVE-2017-12932 (Other, published 2017-08-18T03:29Z)
Description: 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.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-416 \u2014 Use After Free
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.
Repository: https://github.com/php/php-src
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 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.


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;