Skip to content

Commit d02f953

Browse files
nikicweltling
authored andcommitted
Fixed bug #74101 and bug #74614
1 parent d0b7eed commit d02f953

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
@@ -503,6 +503,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
503503
&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
504504

505505
ht = Z_OBJPROP_P(rval);
506+
if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
507+
return 0;
508+
}
509+
506510
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
507511
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
508512
if (has_wakeup) {
@@ -578,7 +582,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
578582
start = cursor;
579583

580584

581-
#line 582 "ext/standard/var_unserializer.c"
585+
#line 586 "ext/standard/var_unserializer.c"
582586
{
583587
YYCTYPE yych;
584588
static const unsigned char yybm[] = {
@@ -636,9 +640,9 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
636640
yy2:
637641
++YYCURSOR;
638642
yy3:
639-
#line 959 "ext/standard/var_unserializer.re"
643+
#line 963 "ext/standard/var_unserializer.re"
640644
{ return 0; }
641-
#line 642 "ext/standard/var_unserializer.c"
645+
#line 646 "ext/standard/var_unserializer.c"
642646
yy4:
643647
yych = *(YYMARKER = ++YYCURSOR);
644648
if (yych == ':') goto yy17;
@@ -685,13 +689,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
685689
goto yy3;
686690
yy15:
687691
++YYCURSOR;
688-
#line 953 "ext/standard/var_unserializer.re"
692+
#line 957 "ext/standard/var_unserializer.re"
689693
{
690694
/* this is the case where we have less data than planned */
691695
php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
692696
return 0; /* not sure if it should be 0 or 1 here? */
693697
}
694-
#line 695 "ext/standard/var_unserializer.c"
698+
#line 699 "ext/standard/var_unserializer.c"
695699
yy17:
696700
yych = *++YYCURSOR;
697701
if (yybm[0+yych] & 128) {
@@ -703,13 +707,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
703707
goto yy3;
704708
yy19:
705709
++YYCURSOR;
706-
#line 637 "ext/standard/var_unserializer.re"
710+
#line 641 "ext/standard/var_unserializer.re"
707711
{
708712
*p = YYCURSOR;
709713
ZVAL_NULL(rval);
710714
return 1;
711715
}
712-
#line 713 "ext/standard/var_unserializer.c"
716+
#line 717 "ext/standard/var_unserializer.c"
713717
yy21:
714718
yych = *++YYCURSOR;
715719
if (yych <= ',') {
@@ -959,7 +963,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
959963
goto yy18;
960964
yy63:
961965
++YYCURSOR;
962-
#line 586 "ext/standard/var_unserializer.re"
966+
#line 590 "ext/standard/var_unserializer.re"
963967
{
964968
zend_long id;
965969

@@ -985,7 +989,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
985989

986990
return 1;
987991
}
988-
#line 989 "ext/standard/var_unserializer.c"
992+
#line 993 "ext/standard/var_unserializer.c"
989993
yy65:
990994
yych = *++YYCURSOR;
991995
if (yych == '"') goto yy84;
@@ -996,13 +1000,13 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
9961000
goto yy18;
9971001
yy67:
9981002
++YYCURSOR;
999-
#line 643 "ext/standard/var_unserializer.re"
1003+
#line 647 "ext/standard/var_unserializer.re"
10001004
{
10011005
*p = YYCURSOR;
10021006
ZVAL_BOOL(rval, parse_iv(start + 2));
10031007
return 1;
10041008
}
1005-
#line 1006 "ext/standard/var_unserializer.c"
1009+
#line 1010 "ext/standard/var_unserializer.c"
10061010
yy69:
10071011
++YYCURSOR;
10081012
if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
@@ -1022,7 +1026,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10221026
}
10231027
yy71:
10241028
++YYCURSOR;
1025-
#line 691 "ext/standard/var_unserializer.re"
1029+
#line 695 "ext/standard/var_unserializer.re"
10261030
{
10271031
#if SIZEOF_ZEND_LONG == 4
10281032
use_double:
@@ -1031,7 +1035,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10311035
ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
10321036
return 1;
10331037
}
1034-
#line 1035 "ext/standard/var_unserializer.c"
1038+
#line 1039 "ext/standard/var_unserializer.c"
10351039
yy73:
10361040
yych = *++YYCURSOR;
10371041
if (yych <= ',') {
@@ -1053,7 +1057,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10531057
goto yy18;
10541058
yy76:
10551059
++YYCURSOR;
1056-
#line 649 "ext/standard/var_unserializer.re"
1060+
#line 653 "ext/standard/var_unserializer.re"
10571061
{
10581062
#if SIZEOF_ZEND_LONG == 4
10591063
int digits = YYCURSOR - start - 3;
@@ -1079,14 +1083,14 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
10791083
ZVAL_LONG(rval, parse_iv(start + 2));
10801084
return 1;
10811085
}
1082-
#line 1083 "ext/standard/var_unserializer.c"
1086+
#line 1087 "ext/standard/var_unserializer.c"
10831087
yy78:
10841088
yych = *++YYCURSOR;
10851089
if (yych == '"') goto yy92;
10861090
goto yy18;
10871091
yy79:
10881092
++YYCURSOR;
1089-
#line 612 "ext/standard/var_unserializer.re"
1093+
#line 616 "ext/standard/var_unserializer.re"
10901094
{
10911095
zend_long id;
10921096

@@ -1111,14 +1115,14 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
11111115

11121116
return 1;
11131117
}
1114-
#line 1115 "ext/standard/var_unserializer.c"
1118+
#line 1119 "ext/standard/var_unserializer.c"
11151119
yy81:
11161120
yych = *++YYCURSOR;
11171121
if (yych == '"') goto yy94;
11181122
goto yy18;
11191123
yy82:
11201124
++YYCURSOR;
1121-
#line 801 "ext/standard/var_unserializer.re"
1125+
#line 805 "ext/standard/var_unserializer.re"
11221126
{
11231127
size_t len, len2, len3, maxlen;
11241128
zend_long elements;
@@ -1270,10 +1274,10 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
12701274

12711275
return object_common2(UNSERIALIZE_PASSTHRU, elements);
12721276
}
1273-
#line 1274 "ext/standard/var_unserializer.c"
1277+
#line 1278 "ext/standard/var_unserializer.c"
12741278
yy84:
12751279
++YYCURSOR;
1276-
#line 732 "ext/standard/var_unserializer.re"
1280+
#line 736 "ext/standard/var_unserializer.re"
12771281
{
12781282
size_t len, maxlen;
12791283
zend_string *str;
@@ -1307,17 +1311,17 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13071311
ZVAL_STR(rval, str);
13081312
return 1;
13091313
}
1310-
#line 1311 "ext/standard/var_unserializer.c"
1314+
#line 1315 "ext/standard/var_unserializer.c"
13111315
yy86:
13121316
++YYCURSOR;
1313-
#line 766 "ext/standard/var_unserializer.re"
1317+
#line 770 "ext/standard/var_unserializer.re"
13141318
{
13151319
zend_long elements = parse_iv(start + 2);
13161320
/* use iv() not uiv() in order to check data range */
13171321
*p = YYCURSOR;
13181322
if (!var_hash) return 0;
13191323

1320-
if (elements < 0) {
1324+
if (elements < 0 || elements >= HT_MAX_SIZE) {
13211325
return 0;
13221326
}
13231327

@@ -1334,7 +1338,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13341338

13351339
return finish_nested_data(UNSERIALIZE_PASSTHRU);
13361340
}
1337-
#line 1338 "ext/standard/var_unserializer.c"
1341+
#line 1342 "ext/standard/var_unserializer.c"
13381342
yy88:
13391343
yych = *++YYCURSOR;
13401344
if (yych <= ',') {
@@ -1359,21 +1363,21 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
13591363
goto yy18;
13601364
yy92:
13611365
++YYCURSOR;
1362-
#line 790 "ext/standard/var_unserializer.re"
1366+
#line 794 "ext/standard/var_unserializer.re"
13631367
{
13641368
long elements;
13651369
if (!var_hash) return 0;
13661370

13671371
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
1368-
if (elements < 0) {
1372+
if (elements < 0 || elements >= HT_MAX_SIZE) {
13691373
return 0;
13701374
}
13711375
return object_common2(UNSERIALIZE_PASSTHRU, elements);
13721376
}
1373-
#line 1374 "ext/standard/var_unserializer.c"
1377+
#line 1378 "ext/standard/var_unserializer.c"
13741378
yy94:
13751379
++YYCURSOR;
1376-
#line 700 "ext/standard/var_unserializer.re"
1380+
#line 704 "ext/standard/var_unserializer.re"
13771381
{
13781382
size_t len, maxlen;
13791383
char *str;
@@ -1405,15 +1409,15 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
14051409
ZVAL_STRINGL(rval, str, len);
14061410
return 1;
14071411
}
1408-
#line 1409 "ext/standard/var_unserializer.c"
1412+
#line 1413 "ext/standard/var_unserializer.c"
14091413
yy96:
14101414
yych = *++YYCURSOR;
14111415
if (yych <= '/') goto yy18;
14121416
if (yych <= '9') goto yy89;
14131417
goto yy18;
14141418
yy97:
14151419
++YYCURSOR;
1416-
#line 675 "ext/standard/var_unserializer.re"
1420+
#line 679 "ext/standard/var_unserializer.re"
14171421
{
14181422
*p = YYCURSOR;
14191423

@@ -1429,9 +1433,9 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
14291433

14301434
return 1;
14311435
}
1432-
#line 1433 "ext/standard/var_unserializer.c"
1436+
#line 1437 "ext/standard/var_unserializer.c"
14331437
}
1434-
#line 961 "ext/standard/var_unserializer.re"
1438+
#line 965 "ext/standard/var_unserializer.re"
14351439

14361440

14371441
return 0;

ext/standard/var_unserializer.re

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

509509
ht = Z_OBJPROP_P(rval);
510+
if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
511+
return 0;
512+
}
513+
510514
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
511515
if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
512516
if (has_wakeup) {
@@ -769,7 +773,7 @@ use_double:
769773
*p = YYCURSOR;
770774
if (!var_hash) return 0;
771775
772-
if (elements < 0) {
776+
if (elements < 0 || elements >= HT_MAX_SIZE) {
773777
return 0;
774778
}
775779
@@ -792,7 +796,7 @@ use_double:
792796
if (!var_hash) return 0;
793797

794798
elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
795-
if (elements < 0) {
799+
if (elements < 0 || elements >= HT_MAX_SIZE) {
796800
return 0;
797801
}
798802
return object_common2(UNSERIALIZE_PASSTHRU, elements);

0 commit comments

Comments
 (0)