Skip to content

Commit 2dc2400

Browse files
committed
Merge branch 'PHP-7.0' into PHP-7.1
* PHP-7.0: Fixed bug #74101 and bug #74614
2 parents 9d1575c + d02f953 commit 2dc2400

File tree

4 files changed

+62
-34
lines changed

4 files changed

+62
-34
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Bug #74101: Unserialize Heap Use-After-Free (READ: 1) in zval_get_type
3+
--FILE--
4+
<?php
5+
$s = 'O:9:"Exception":799999999999999999999999999997:0i:0;a:0:{}i:2;i:0;i:0;R:2;';
6+
var_dump(unserialize($s));
7+
?>
8+
--EXPECTF--
9+
Notice: unserialize(): Error at offset 48 of 74 bytes in %s on line %d
10+
bool(false)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Bug #74614: Use-after-free in PHP7's unserialize()
3+
--FILE--
4+
<?php
5+
6+
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;}');
7+
8+
?>
9+
--EXPECTF--
10+
Notice: unserialize(): Error at offset 38 of 113 bytes in %s on line %d

ext/standard/var_unserializer.c

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
548548
&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
549549

550550
ht = Z_OBJPROP_P(rval);
551+
if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
552+
return 0;
553+
}
554+
551555
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
552556
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
553557
if (has_wakeup) {
@@ -617,7 +621,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
617621
start = cursor;
618622

619623

620-
#line 621 "ext/standard/var_unserializer.c"
624+
#line 625 "ext/standard/var_unserializer.c"
621625
{
622626
YYCTYPE yych;
623627
static const unsigned char yybm[] = {
@@ -675,9 +679,9 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
675679
yy2:
676680
++YYCURSOR;
677681
yy3:
678-
#line 998 "ext/standard/var_unserializer.re"
682+
#line 1002 "ext/standard/var_unserializer.re"
679683
{ return 0; }
680-
#line 681 "ext/standard/var_unserializer.c"
684+
#line 685 "ext/standard/var_unserializer.c"
681685
yy4:
682686
yych = *(YYMARKER = ++YYCURSOR);
683687
if (yych == ':') goto yy17;
@@ -724,13 +728,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
724728
goto yy3;
725729
yy15:
726730
++YYCURSOR;
727-
#line 992 "ext/standard/var_unserializer.re"
731+
#line 996 "ext/standard/var_unserializer.re"
728732
{
729733
/* this is the case where we have less data than planned */
730734
php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
731735
return 0; /* not sure if it should be 0 or 1 here? */
732736
}
733-
#line 734 "ext/standard/var_unserializer.c"
737+
#line 738 "ext/standard/var_unserializer.c"
734738
yy17:
735739
yych = *++YYCURSOR;
736740
if (yybm[0+yych] & 128) {
@@ -742,13 +746,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
742746
goto yy3;
743747
yy19:
744748
++YYCURSOR;
745-
#line 676 "ext/standard/var_unserializer.re"
749+
#line 680 "ext/standard/var_unserializer.re"
746750
{
747751
*p = YYCURSOR;
748752
ZVAL_NULL(rval);
749753
return 1;
750754
}
751-
#line 752 "ext/standard/var_unserializer.c"
755+
#line 756 "ext/standard/var_unserializer.c"
752756
yy21:
753757
yych = *++YYCURSOR;
754758
if (yych <= ',') {
@@ -998,7 +1002,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
9981002
goto yy18;
9991003
yy63:
10001004
++YYCURSOR;
1001-
#line 625 "ext/standard/var_unserializer.re"
1005+
#line 629 "ext/standard/var_unserializer.re"
10021006
{
10031007
zend_long id;
10041008

@@ -1024,7 +1028,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10241028

10251029
return 1;
10261030
}
1027-
#line 1028 "ext/standard/var_unserializer.c"
1031+
#line 1032 "ext/standard/var_unserializer.c"
10281032
yy65:
10291033
yych = *++YYCURSOR;
10301034
if (yych == '"') goto yy84;
@@ -1035,13 +1039,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10351039
goto yy18;
10361040
yy67:
10371041
++YYCURSOR;
1038-
#line 682 "ext/standard/var_unserializer.re"
1042+
#line 686 "ext/standard/var_unserializer.re"
10391043
{
10401044
*p = YYCURSOR;
10411045
ZVAL_BOOL(rval, parse_iv(start + 2));
10421046
return 1;
10431047
}
1044-
#line 1045 "ext/standard/var_unserializer.c"
1048+
#line 1049 "ext/standard/var_unserializer.c"
10451049
yy69:
10461050
++YYCURSOR;
10471051
if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
@@ -1061,7 +1065,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10611065
}
10621066
yy71:
10631067
++YYCURSOR;
1064-
#line 730 "ext/standard/var_unserializer.re"
1068+
#line 734 "ext/standard/var_unserializer.re"
10651069
{
10661070
#if SIZEOF_ZEND_LONG == 4
10671071
use_double:
@@ -1070,7 +1074,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10701074
ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
10711075
return 1;
10721076
}
1073-
#line 1074 "ext/standard/var_unserializer.c"
1077+
#line 1078 "ext/standard/var_unserializer.c"
10741078
yy73:
10751079
yych = *++YYCURSOR;
10761080
if (yych <= ',') {
@@ -1092,7 +1096,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10921096
goto yy18;
10931097
yy76:
10941098
++YYCURSOR;
1095-
#line 688 "ext/standard/var_unserializer.re"
1099+
#line 692 "ext/standard/var_unserializer.re"
10961100
{
10971101
#if SIZEOF_ZEND_LONG == 4
10981102
int digits = YYCURSOR - start - 3;
@@ -1118,14 +1122,14 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
11181122
ZVAL_LONG(rval, parse_iv(start + 2));
11191123
return 1;
11201124
}
1121-
#line 1122 "ext/standard/var_unserializer.c"
1125+
#line 1126 "ext/standard/var_unserializer.c"
11221126
yy78:
11231127
yych = *++YYCURSOR;
11241128
if (yych == '"') goto yy92;
11251129
goto yy18;
11261130
yy79:
11271131
++YYCURSOR;
1128-
#line 651 "ext/standard/var_unserializer.re"
1132+
#line 655 "ext/standard/var_unserializer.re"
11291133
{
11301134
zend_long id;
11311135

@@ -1150,14 +1154,14 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
11501154

11511155
return 1;
11521156
}
1153-
#line 1154 "ext/standard/var_unserializer.c"
1157+
#line 1158 "ext/standard/var_unserializer.c"
11541158
yy81:
11551159
yych = *++YYCURSOR;
11561160
if (yych == '"') goto yy94;
11571161
goto yy18;
11581162
yy82:
11591163
++YYCURSOR;
1160-
#line 840 "ext/standard/var_unserializer.re"
1164+
#line 844 "ext/standard/var_unserializer.re"
11611165
{
11621166
size_t len, len2, len3, maxlen;
11631167
zend_long elements;
@@ -1309,10 +1313,10 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13091313

13101314
return object_common2(UNSERIALIZE_PASSTHRU, elements);
13111315
}
1312-
#line 1313 "ext/standard/var_unserializer.c"
1316+
#line 1317 "ext/standard/var_unserializer.c"
13131317
yy84:
13141318
++YYCURSOR;
1315-
#line 771 "ext/standard/var_unserializer.re"
1319+
#line 775 "ext/standard/var_unserializer.re"
13161320
{
13171321
size_t len, maxlen;
13181322
zend_string *str;
@@ -1346,17 +1350,17 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13461350
ZVAL_STR(rval, str);
13471351
return 1;
13481352
}
1349-
#line 1350 "ext/standard/var_unserializer.c"
1353+
#line 1354 "ext/standard/var_unserializer.c"
13501354
yy86:
13511355
++YYCURSOR;
1352-
#line 805 "ext/standard/var_unserializer.re"
1356+
#line 809 "ext/standard/var_unserializer.re"
13531357
{
13541358
zend_long elements = parse_iv(start + 2);
13551359
/* use iv() not uiv() in order to check data range */
13561360
*p = YYCURSOR;
13571361
if (!var_hash) return 0;
13581362

1359-
if (elements < 0) {
1363+
if (elements < 0 || elements >= HT_MAX_SIZE) {
13601364
return 0;
13611365
}
13621366

@@ -1373,7 +1377,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13731377

13741378
return finish_nested_data(UNSERIALIZE_PASSTHRU);
13751379
}
1376-
#line 1377 "ext/standard/var_unserializer.c"
1380+
#line 1381 "ext/standard/var_unserializer.c"
13771381
yy88:
13781382
yych = *++YYCURSOR;
13791383
if (yych <= ',') {
@@ -1398,21 +1402,21 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13981402
goto yy18;
13991403
yy92:
14001404
++YYCURSOR;
1401-
#line 829 "ext/standard/var_unserializer.re"
1405+
#line 833 "ext/standard/var_unserializer.re"
14021406
{
14031407
long elements;
14041408
if (!var_hash) return 0;
14051409

14061410
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
1407-
if (elements < 0) {
1411+
if (elements < 0 || elements >= HT_MAX_SIZE) {
14081412
return 0;
14091413
}
14101414
return object_common2(UNSERIALIZE_PASSTHRU, elements);
14111415
}
1412-
#line 1413 "ext/standard/var_unserializer.c"
1416+
#line 1417 "ext/standard/var_unserializer.c"
14131417
yy94:
14141418
++YYCURSOR;
1415-
#line 739 "ext/standard/var_unserializer.re"
1419+
#line 743 "ext/standard/var_unserializer.re"
14161420
{
14171421
size_t len, maxlen;
14181422
char *str;
@@ -1444,15 +1448,15 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
14441448
ZVAL_STRINGL(rval, str, len);
14451449
return 1;
14461450
}
1447-
#line 1448 "ext/standard/var_unserializer.c"
1451+
#line 1452 "ext/standard/var_unserializer.c"
14481452
yy96:
14491453
yych = *++YYCURSOR;
14501454
if (yych <= '/') goto yy18;
14511455
if (yych <= '9') goto yy89;
14521456
goto yy18;
14531457
yy97:
14541458
++YYCURSOR;
1455-
#line 714 "ext/standard/var_unserializer.re"
1459+
#line 718 "ext/standard/var_unserializer.re"
14561460
{
14571461
*p = YYCURSOR;
14581462

@@ -1468,9 +1472,9 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
14681472

14691473
return 1;
14701474
}
1471-
#line 1472 "ext/standard/var_unserializer.c"
1475+
#line 1476 "ext/standard/var_unserializer.c"
14721476
}
1473-
#line 1000 "ext/standard/var_unserializer.re"
1477+
#line 1004 "ext/standard/var_unserializer.re"
14741478

14751479

14761480
return 0;

ext/standard/var_unserializer.re

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
552552
&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
553553

554554
ht = Z_OBJPROP_P(rval);
555+
if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
556+
return 0;
557+
}
558+
555559
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
556560
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
557561
if (has_wakeup) {
@@ -808,7 +812,7 @@ use_double:
808812
*p = YYCURSOR;
809813
if (!var_hash) return 0;
810814
811-
if (elements < 0) {
815+
if (elements < 0 || elements >= HT_MAX_SIZE) {
812816
return 0;
813817
}
814818
@@ -831,7 +835,7 @@ use_double:
831835
if (!var_hash) return 0;
832836

833837
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
834-
if (elements < 0) {
838+
if (elements < 0 || elements >= HT_MAX_SIZE) {
835839
return 0;
836840
}
837841
return object_common2(UNSERIALIZE_PASSTHRU, elements);

0 commit comments

Comments
 (0)