summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatol Belski <ab@php.net>2017-07-04 10:42:48 +0200
committerJoe Watkins <krakjoe@php.net>2017-07-04 20:07:00 +0100
commit2c567eae283308bbc1cb9836546b8dbf0146932c (patch)
treed38ecb3e814bf0ef4c4df474dd0ffdd166a14d2d
parent3d7b2117e0cf2b0f76fcdd8080d4d5c82d0f82f8 (diff)
downloadphp-git-2c567eae283308bbc1cb9836546b8dbf0146932c.tar.gz
Merge branch 'PHP-7.0' into PHP-7.1
* PHP-7.0: Fixed bug #74101 and bug #74614
-rw-r--r--ext/standard/tests/serialize/bug74101.phpt10
-rw-r--r--ext/standard/tests/serialize/bug74614.phpt10
-rw-r--r--ext/standard/var_unserializer.c68
-rw-r--r--ext/standard/var_unserializer.re8
4 files changed, 62 insertions, 34 deletions
diff --git a/ext/standard/tests/serialize/bug74101.phpt b/ext/standard/tests/serialize/bug74101.phpt
new file mode 100644
index 0000000000..a414060f5c
--- /dev/null
+++ b/ext/standard/tests/serialize/bug74101.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Bug #74101: Unserialize Heap Use-After-Free (READ: 1) in zval_get_type
+--FILE--
+<?php
+$s = 'O:9:"Exception":799999999999999999999999999997:0i:0;a:0:{}i:2;i:0;i:0;R:2;';
+var_dump(unserialize($s));
+?>
+--EXPECTF--
+Notice: unserialize(): Error at offset 48 of 74 bytes in %s on line %d
+bool(false)
diff --git a/ext/standard/tests/serialize/bug74614.phpt b/ext/standard/tests/serialize/bug74614.phpt
new file mode 100644
index 0000000000..ae962628e9
--- /dev/null
+++ b/ext/standard/tests/serialize/bug74614.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Bug #74614: Use-after-free in PHP7's unserialize()
+--FILE--
+<?php
+
+unserialize('a:3020000000000000000000000000000001:{i:0;a:0:{}i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;R:2;}');
+
+?>
+--EXPECTF--
+Notice: unserialize(): Error at offset 38 of 113 bytes in %s on line %d
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index 609ed45bd1..ac024fdf42 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -548,6 +548,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
ht = Z_OBJPROP_P(rval);
+ if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
+ return 0;
+ }
+
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
if (has_wakeup) {
@@ -617,7 +621,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
start = cursor;
-#line 621 "ext/standard/var_unserializer.c"
+#line 625 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -675,9 +679,9 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
yy2:
++YYCURSOR;
yy3:
-#line 998 "ext/standard/var_unserializer.re"
+#line 1002 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 681 "ext/standard/var_unserializer.c"
+#line 685 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy17;
@@ -724,13 +728,13 @@ yy14:
goto yy3;
yy15:
++YYCURSOR;
-#line 992 "ext/standard/var_unserializer.re"
+#line 996 "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 734 "ext/standard/var_unserializer.c"
+#line 738 "ext/standard/var_unserializer.c"
yy17:
yych = *++YYCURSOR;
if (yybm[0+yych] & 128) {
@@ -742,13 +746,13 @@ yy18:
goto yy3;
yy19:
++YYCURSOR;
-#line 676 "ext/standard/var_unserializer.re"
+#line 680 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_NULL(rval);
return 1;
}
-#line 752 "ext/standard/var_unserializer.c"
+#line 756 "ext/standard/var_unserializer.c"
yy21:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -998,7 +1002,7 @@ yy62:
goto yy18;
yy63:
++YYCURSOR;
-#line 625 "ext/standard/var_unserializer.re"
+#line 629 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1024,7 +1028,7 @@ yy63:
return 1;
}
-#line 1028 "ext/standard/var_unserializer.c"
+#line 1032 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych == '"') goto yy84;
@@ -1035,13 +1039,13 @@ yy66:
goto yy18;
yy67:
++YYCURSOR;
-#line 682 "ext/standard/var_unserializer.re"
+#line 686 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_BOOL(rval, parse_iv(start + 2));
return 1;
}
-#line 1045 "ext/standard/var_unserializer.c"
+#line 1049 "ext/standard/var_unserializer.c"
yy69:
++YYCURSOR;
if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
@@ -1061,7 +1065,7 @@ yy69:
}
yy71:
++YYCURSOR;
-#line 730 "ext/standard/var_unserializer.re"
+#line 734 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
use_double:
@@ -1070,7 +1074,7 @@ use_double:
ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 1074 "ext/standard/var_unserializer.c"
+#line 1078 "ext/standard/var_unserializer.c"
yy73:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1092,7 +1096,7 @@ yy75:
goto yy18;
yy76:
++YYCURSOR;
-#line 688 "ext/standard/var_unserializer.re"
+#line 692 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1118,14 +1122,14 @@ yy76:
ZVAL_LONG(rval, parse_iv(start + 2));
return 1;
}
-#line 1122 "ext/standard/var_unserializer.c"
+#line 1126 "ext/standard/var_unserializer.c"
yy78:
yych = *++YYCURSOR;
if (yych == '"') goto yy92;
goto yy18;
yy79:
++YYCURSOR;
-#line 651 "ext/standard/var_unserializer.re"
+#line 655 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1150,14 +1154,14 @@ yy79:
return 1;
}
-#line 1154 "ext/standard/var_unserializer.c"
+#line 1158 "ext/standard/var_unserializer.c"
yy81:
yych = *++YYCURSOR;
if (yych == '"') goto yy94;
goto yy18;
yy82:
++YYCURSOR;
-#line 840 "ext/standard/var_unserializer.re"
+#line 844 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
zend_long elements;
@@ -1309,10 +1313,10 @@ yy82:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 1313 "ext/standard/var_unserializer.c"
+#line 1317 "ext/standard/var_unserializer.c"
yy84:
++YYCURSOR;
-#line 771 "ext/standard/var_unserializer.re"
+#line 775 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
zend_string *str;
@@ -1346,17 +1350,17 @@ yy84:
ZVAL_STR(rval, str);
return 1;
}
-#line 1350 "ext/standard/var_unserializer.c"
+#line 1354 "ext/standard/var_unserializer.c"
yy86:
++YYCURSOR;
-#line 805 "ext/standard/var_unserializer.re"
+#line 809 "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) {
+ if (elements < 0 || elements >= HT_MAX_SIZE) {
return 0;
}
@@ -1373,7 +1377,7 @@ yy86:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 1377 "ext/standard/var_unserializer.c"
+#line 1381 "ext/standard/var_unserializer.c"
yy88:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1398,21 +1402,21 @@ yy91:
goto yy18;
yy92:
++YYCURSOR;
-#line 829 "ext/standard/var_unserializer.re"
+#line 833 "ext/standard/var_unserializer.re"
{
long elements;
if (!var_hash) return 0;
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
- if (elements < 0) {
+ if (elements < 0 || elements >= HT_MAX_SIZE) {
return 0;
}
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 1413 "ext/standard/var_unserializer.c"
+#line 1417 "ext/standard/var_unserializer.c"
yy94:
++YYCURSOR;
-#line 739 "ext/standard/var_unserializer.re"
+#line 743 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -1444,7 +1448,7 @@ yy94:
ZVAL_STRINGL(rval, str, len);
return 1;
}
-#line 1448 "ext/standard/var_unserializer.c"
+#line 1452 "ext/standard/var_unserializer.c"
yy96:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1452,7 +1456,7 @@ yy96:
goto yy18;
yy97:
++YYCURSOR;
-#line 714 "ext/standard/var_unserializer.re"
+#line 718 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
@@ -1468,9 +1472,9 @@ yy97:
return 1;
}
-#line 1472 "ext/standard/var_unserializer.c"
+#line 1476 "ext/standard/var_unserializer.c"
}
-#line 1000 "ext/standard/var_unserializer.re"
+#line 1004 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 8906c353b3..2b32e6b977 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -552,6 +552,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
ht = Z_OBJPROP_P(rval);
+ if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
+ return 0;
+ }
+
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
if (has_wakeup) {
@@ -808,7 +812,7 @@ use_double:
*p = YYCURSOR;
if (!var_hash) return 0;
- if (elements < 0) {
+ if (elements < 0 || elements >= HT_MAX_SIZE) {
return 0;
}
@@ -831,7 +835,7 @@ use_double:
if (!var_hash) return 0;
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
- if (elements < 0) {
+ if (elements < 0 || elements >= HT_MAX_SIZE) {
return 0;
}
return object_common2(UNSERIALIZE_PASSTHRU, elements);