-
-
Save anonymous/fbc13007bdd5352842d5e071b47aa897 to your computer and use it in GitHub Desktop.
Patch for 73825
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit 16b3003ffc6393e250f069aa28a78dc5a2c064b2 | |
Author: Stanislav Malyshev <stas@php.net> | |
Date: Fri Dec 30 16:59:46 2016 -0800 | |
Fix bug #73825 - Heap out of bounds read on unserialize in finish_nested_data() | |
diff --git a/ext/standard/tests/serialize/bug73825.phpt b/ext/standard/tests/serialize/bug73825.phpt | |
new file mode 100644 | |
index 0000000..adbfca1 | |
--- /dev/null | |
+++ b/ext/standard/tests/serialize/bug73825.phpt | |
@@ -0,0 +1,12 @@ | |
+--TEST-- | |
+Bug #73825 Heap out of bounds read on unserialize in finish_nested_data() | |
+--FILE-- | |
+<?php | |
+$obj = unserialize('O:8:"00000000":'); | |
+var_dump($obj); | |
+?> | |
+--EXPECTF-- | |
+Warning: Bad unserialize data in %sbug73825.php on line %d | |
+ | |
+Notice: unserialize(): Error at offset 13 of 15 bytes in %sbug73825.php on line %d | |
+bool(false) | |
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c | |
index 5491492..efb0942 100644 | |
--- a/ext/standard/var_unserializer.c | |
+++ b/ext/standard/var_unserializer.c | |
@@ -405,6 +405,11 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) | |
{ | |
long elements; | |
+ if( *p >= max - 2) { | |
+ zend_error(E_WARNING, "Bad unserialize data"); | |
+ return -1; | |
+ } | |
+ | |
elements = parse_iv2((*p) + 2, p); | |
(*p) += 2; | |
@@ -415,7 +420,7 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) | |
/* If this class implements Serializable, it should not land here but in object_custom(). The passed string | |
obviously doesn't descend from the regular serializer. */ | |
zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); | |
- return 0; | |
+ return -1; | |
} | |
return elements; | |
@@ -492,7 +497,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) | |
-#line 496 "ext/standard/var_unserializer.c" | |
+#line 501 "ext/standard/var_unserializer.c" | |
{ | |
YYCTYPE yych; | |
static const unsigned char yybm[] = { | |
@@ -552,9 +557,9 @@ yy2: | |
yych = *(YYMARKER = ++YYCURSOR); | |
if (yych == ':') goto yy95; | |
yy3: | |
-#line 861 "ext/standard/var_unserializer.re" | |
+#line 875 "ext/standard/var_unserializer.re" | |
{ return 0; } | |
-#line 558 "ext/standard/var_unserializer.c" | |
+#line 563 "ext/standard/var_unserializer.c" | |
yy4: | |
yych = *(YYMARKER = ++YYCURSOR); | |
if (yych == ':') goto yy89; | |
@@ -597,13 +602,13 @@ yy13: | |
goto yy3; | |
yy14: | |
++YYCURSOR; | |
-#line 855 "ext/standard/var_unserializer.re" | |
+#line 869 "ext/standard/var_unserializer.re" | |
{ | |
/* this is the case where we have less data than planned */ | |
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); | |
return 0; /* not sure if it should be 0 or 1 here? */ | |
} | |
-#line 607 "ext/standard/var_unserializer.c" | |
+#line 612 "ext/standard/var_unserializer.c" | |
yy16: | |
yych = *++YYCURSOR; | |
goto yy3; | |
@@ -634,7 +639,7 @@ yy20: | |
yych = *++YYCURSOR; | |
if (yych != '"') goto yy18; | |
++YYCURSOR; | |
-#line 708 "ext/standard/var_unserializer.re" | |
+#line 717 "ext/standard/var_unserializer.re" | |
{ | |
size_t len, len2, len3, maxlen; | |
long elements; | |
@@ -774,6 +779,11 @@ yy20: | |
elements = object_common1(UNSERIALIZE_PASSTHRU, ce); | |
+ if (elements < 0) { | |
+ efree(class_name); | |
+ return 0; | |
+ } | |
+ | |
if (incomplete_class) { | |
php_store_class_name(*rval, class_name, len2); | |
} | |
@@ -781,7 +791,7 @@ yy20: | |
return object_common2(UNSERIALIZE_PASSTHRU, elements); | |
} | |
-#line 785 "ext/standard/var_unserializer.c" | |
+#line 795 "ext/standard/var_unserializer.c" | |
yy25: | |
yych = *++YYCURSOR; | |
if (yych <= ',') { | |
@@ -806,16 +816,20 @@ yy27: | |
yych = *++YYCURSOR; | |
if (yych != '"') goto yy18; | |
++YYCURSOR; | |
-#line 699 "ext/standard/var_unserializer.re" | |
+#line 704 "ext/standard/var_unserializer.re" | |
{ | |
+ long elements; | |
if (!var_hash) return 0; | |
INIT_PZVAL(*rval); | |
- return object_common2(UNSERIALIZE_PASSTHRU, | |
- object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); | |
+ elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR); | |
+ if (elements < 0) { | |
+ return 0; | |
+ } | |
+ return object_common2(UNSERIALIZE_PASSTHRU, elements); | |
} | |
-#line 819 "ext/standard/var_unserializer.c" | |
+#line 833 "ext/standard/var_unserializer.c" | |
yy32: | |
yych = *++YYCURSOR; | |
if (yych == '+') goto yy33; | |
@@ -836,7 +850,7 @@ yy34: | |
yych = *++YYCURSOR; | |
if (yych != '{') goto yy18; | |
++YYCURSOR; | |
-#line 678 "ext/standard/var_unserializer.re" | |
+#line 683 "ext/standard/var_unserializer.re" | |
{ | |
long elements = parse_iv(start + 2); | |
/* use iv() not uiv() in order to check data range */ | |
@@ -857,7 +871,7 @@ yy34: | |
return finish_nested_data(UNSERIALIZE_PASSTHRU); | |
} | |
-#line 861 "ext/standard/var_unserializer.c" | |
+#line 875 "ext/standard/var_unserializer.c" | |
yy39: | |
yych = *++YYCURSOR; | |
if (yych == '+') goto yy40; | |
@@ -878,7 +892,7 @@ yy41: | |
yych = *++YYCURSOR; | |
if (yych != '"') goto yy18; | |
++YYCURSOR; | |
-#line 643 "ext/standard/var_unserializer.re" | |
+#line 648 "ext/standard/var_unserializer.re" | |
{ | |
size_t len, maxlen; | |
char *str; | |
@@ -913,7 +927,7 @@ yy41: | |
ZVAL_STRINGL(*rval, str, len, 0); | |
return 1; | |
} | |
-#line 917 "ext/standard/var_unserializer.c" | |
+#line 931 "ext/standard/var_unserializer.c" | |
yy46: | |
yych = *++YYCURSOR; | |
if (yych == '+') goto yy47; | |
@@ -934,7 +948,7 @@ yy48: | |
yych = *++YYCURSOR; | |
if (yych != '"') goto yy18; | |
++YYCURSOR; | |
-#line 610 "ext/standard/var_unserializer.re" | |
+#line 615 "ext/standard/var_unserializer.re" | |
{ | |
size_t len, maxlen; | |
char *str; | |
@@ -967,7 +981,7 @@ yy48: | |
ZVAL_STRINGL(*rval, str, len, 1); | |
return 1; | |
} | |
-#line 971 "ext/standard/var_unserializer.c" | |
+#line 985 "ext/standard/var_unserializer.c" | |
yy53: | |
yych = *++YYCURSOR; | |
if (yych <= '/') { | |
@@ -1055,7 +1069,7 @@ yy61: | |
} | |
yy63: | |
++YYCURSOR; | |
-#line 600 "ext/standard/var_unserializer.re" | |
+#line 605 "ext/standard/var_unserializer.re" | |
{ | |
#if SIZEOF_LONG == 4 | |
use_double: | |
@@ -1065,7 +1079,7 @@ use_double: | |
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); | |
return 1; | |
} | |
-#line 1069 "ext/standard/var_unserializer.c" | |
+#line 1083 "ext/standard/var_unserializer.c" | |
yy65: | |
yych = *++YYCURSOR; | |
if (yych <= ',') { | |
@@ -1124,7 +1138,7 @@ yy73: | |
yych = *++YYCURSOR; | |
if (yych != ';') goto yy18; | |
++YYCURSOR; | |
-#line 585 "ext/standard/var_unserializer.re" | |
+#line 590 "ext/standard/var_unserializer.re" | |
{ | |
*p = YYCURSOR; | |
INIT_PZVAL(*rval); | |
@@ -1139,7 +1153,7 @@ yy73: | |
return 1; | |
} | |
-#line 1143 "ext/standard/var_unserializer.c" | |
+#line 1157 "ext/standard/var_unserializer.c" | |
yy76: | |
yych = *++YYCURSOR; | |
if (yych == 'N') goto yy73; | |
@@ -1166,7 +1180,7 @@ yy79: | |
if (yych <= '9') goto yy79; | |
if (yych != ';') goto yy18; | |
++YYCURSOR; | |
-#line 558 "ext/standard/var_unserializer.re" | |
+#line 563 "ext/standard/var_unserializer.re" | |
{ | |
#if SIZEOF_LONG == 4 | |
int digits = YYCURSOR - start - 3; | |
@@ -1193,7 +1207,7 @@ yy79: | |
ZVAL_LONG(*rval, parse_iv(start + 2)); | |
return 1; | |
} | |
-#line 1197 "ext/standard/var_unserializer.c" | |
+#line 1211 "ext/standard/var_unserializer.c" | |
yy83: | |
yych = *++YYCURSOR; | |
if (yych <= '/') goto yy18; | |
@@ -1201,24 +1215,24 @@ yy83: | |
yych = *++YYCURSOR; | |
if (yych != ';') goto yy18; | |
++YYCURSOR; | |
-#line 551 "ext/standard/var_unserializer.re" | |
+#line 556 "ext/standard/var_unserializer.re" | |
{ | |
*p = YYCURSOR; | |
INIT_PZVAL(*rval); | |
ZVAL_BOOL(*rval, parse_iv(start + 2)); | |
return 1; | |
} | |
-#line 1212 "ext/standard/var_unserializer.c" | |
+#line 1226 "ext/standard/var_unserializer.c" | |
yy87: | |
++YYCURSOR; | |
-#line 544 "ext/standard/var_unserializer.re" | |
+#line 549 "ext/standard/var_unserializer.re" | |
{ | |
*p = YYCURSOR; | |
INIT_PZVAL(*rval); | |
ZVAL_NULL(*rval); | |
return 1; | |
} | |
-#line 1222 "ext/standard/var_unserializer.c" | |
+#line 1236 "ext/standard/var_unserializer.c" | |
yy89: | |
yych = *++YYCURSOR; | |
if (yych <= ',') { | |
@@ -1241,7 +1255,7 @@ yy91: | |
if (yych <= '9') goto yy91; | |
if (yych != ';') goto yy18; | |
++YYCURSOR; | |
-#line 521 "ext/standard/var_unserializer.re" | |
+#line 526 "ext/standard/var_unserializer.re" | |
{ | |
long id; | |
@@ -1264,7 +1278,7 @@ yy91: | |
return 1; | |
} | |
-#line 1268 "ext/standard/var_unserializer.c" | |
+#line 1282 "ext/standard/var_unserializer.c" | |
yy95: | |
yych = *++YYCURSOR; | |
if (yych <= ',') { | |
@@ -1287,7 +1301,7 @@ yy97: | |
if (yych <= '9') goto yy97; | |
if (yych != ';') goto yy18; | |
++YYCURSOR; | |
-#line 500 "ext/standard/var_unserializer.re" | |
+#line 505 "ext/standard/var_unserializer.re" | |
{ | |
long id; | |
@@ -1308,9 +1322,9 @@ yy97: | |
return 1; | |
} | |
-#line 1312 "ext/standard/var_unserializer.c" | |
+#line 1326 "ext/standard/var_unserializer.c" | |
} | |
-#line 863 "ext/standard/var_unserializer.re" | |
+#line 877 "ext/standard/var_unserializer.re" | |
return 0; | |
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re | |
index ce84bf5..4cdf313 100644 | |
--- a/ext/standard/var_unserializer.re | |
+++ b/ext/standard/var_unserializer.re | |
@@ -409,6 +409,11 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) | |
{ | |
long elements; | |
+ if( *p >= max - 2) { | |
+ zend_error(E_WARNING, "Bad unserialize data"); | |
+ return -1; | |
+ } | |
+ | |
elements = parse_iv2((*p) + 2, p); | |
(*p) += 2; | |
@@ -419,7 +424,7 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) | |
/* If this class implements Serializable, it should not land here but in object_custom(). The passed string | |
obviously doesn't descend from the regular serializer. */ | |
zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); | |
- return 0; | |
+ return -1; | |
} | |
return elements; | |
@@ -697,12 +702,16 @@ use_double: | |
} | |
"o:" iv ":" ["] { | |
+ long elements; | |
if (!var_hash) return 0; | |
INIT_PZVAL(*rval); | |
- return object_common2(UNSERIALIZE_PASSTHRU, | |
- object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); | |
+ elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR); | |
+ if (elements < 0) { | |
+ return 0; | |
+ } | |
+ return object_common2(UNSERIALIZE_PASSTHRU, elements); | |
} | |
object ":" uiv ":" ["] { | |
@@ -844,6 +853,11 @@ object ":" uiv ":" ["] { | |
elements = object_common1(UNSERIALIZE_PASSTHRU, ce); | |
+ if (elements < 0) { | |
+ efree(class_name); | |
+ return 0; | |
+ } | |
+ | |
if (incomplete_class) { | |
php_store_class_name(*rval, class_name, len2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment