]> granicus.if.org Git - php/commitdiff
Fixed bug #70211 (php 7 ZEND_HASH_IF_FULL_DO_RESIZE use after free)
authorXinchen Hui <laruence@php.net>
Mon, 10 Aug 2015 09:02:16 +0000 (17:02 +0800)
committerXinchen Hui <laruence@php.net>
Mon, 10 Aug 2015 09:02:16 +0000 (17:02 +0800)
NEWS
Zend/zend_hash.c
Zend/zend_hash.h
ext/soap/tests/bug70211.phpt [new file with mode: 0644]
ext/standard/var_unserializer.c
ext/standard/var_unserializer.re

diff --git a/NEWS b/NEWS
index 8e489897db68e762191cd05ecf0babcae9c0a82a..bd0a195fbec7bfcc495787d483c4b09b20284a6a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ PHP                                                                        NEWS
   . Fixed bug #70214 (FASYNC not defined, needs sys/file.h include). (Bob)
 
 - Standard:
+  . Fixed bug #70211 (php 7 ZEND_HASH_IF_FULL_DO_RESIZE use after free).
+    (Laruence)
   . Fixed bug #70208 (Assert breaking access on objects). (Bob)
 
 06 Aug 2015, PHP 7.0.0 Beta 3
index eb5081d56c724d942bb8cba061fa8981424f77aa..0d3932ebd420a96e234394817105ab5616c1417c 100644 (file)
@@ -807,6 +807,20 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
        }
 }
 
+ZEND_API int ZEND_FASTCALL zend_hash_resize(HashTable *ht, uint32_t size) {
+       void *old_data = HT_GET_DATA_ADDR(ht);
+       Bucket *old_buckets = ht->arData;
+
+       HANDLE_BLOCK_INTERRUPTIONS();
+       ht->nTableSize = zend_hash_check_size(size);
+       ht->nTableMask = -ht->nTableSize;
+       HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), ht->u.flags & HASH_FLAG_PERSISTENT));
+       memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
+       pefree(old_data, ht->u.flags & HASH_FLAG_PERSISTENT);
+       zend_hash_rehash(ht);
+       HANDLE_UNBLOCK_INTERRUPTIONS();
+}
+
 ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
 {
        Bucket *p;
index 4a33b959cb44880014f96c1552ad3d0edd168be8..b32309603c96bd72d78afab3091c5f36a0d7971e 100644 (file)
@@ -212,6 +212,7 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_
 #define zend_hash_next_free_element(ht) \
        (ht)->nNextFreeElement
 
+ZEND_API int ZEND_FASTCALL zend_hash_resize(HashTable *ht, uint32_t size);
 ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht);
 
 ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source);
diff --git a/ext/soap/tests/bug70211.phpt b/ext/soap/tests/bug70211.phpt
new file mode 100644 (file)
index 0000000..8346b35
--- /dev/null
@@ -0,0 +1,24 @@
+--TEST--
+Bug #70211 (php 7 ZEND_HASH_IF_FULL_DO_RESIZE use after free)
+--FILE--
+<?php
+
+$addr = 0x4141414141414141;
+
+function ptr2str($ptr)
+{
+    $out = "";
+    for ($i=0; $i<8; $i++) {
+        $out .= chr($ptr & 0xff);
+        $ptr >>= 8;
+    }
+    return $out;
+}
+
+$sf = new SoapFault('1', 'string', 'detail', 'header','line', str_repeat("A",232).ptr2str($addr));
+$ob = unserialize("a:3:{i:0;".serialize($sf).'i:1;r:12;i:2;r:10;}');
+
+var_dump($ob[1]);
+?>
+--EXPECT--
+string(6) "detail"
index ea1c971081eb98cdc2e45119abd03947189fec70..6bc37a8263cbf49e4cda7e558852a2809f1eb3a2 100644 (file)
@@ -470,13 +470,14 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
 {
        zval retval;
        zval fname;
+       HashTable *ht = Z_OBJPROP_P(rval);
 
        if (Z_TYPE_P(rval) != IS_OBJECT) {
                return 0;
        }
 
-       //??? TODO: resize before
-       if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
+       zend_hash_resize(ht, zend_hash_num_elements(ht) + elements);
+       if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
                return 0;
        }
 
@@ -528,7 +529,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
        start = cursor;
 
 
-#line 532 "ext/standard/var_unserializer.c"
+#line 533 "ext/standard/var_unserializer.c"
 {
        YYCTYPE yych;
        static const unsigned char yybm[] = {
@@ -588,9 +589,9 @@ yy2:
        yych = *(YYMARKER = ++YYCURSOR);
        if (yych == ':') goto yy95;
 yy3:
-#line 879 "ext/standard/var_unserializer.re"
+#line 880 "ext/standard/var_unserializer.re"
        { return 0; }
-#line 594 "ext/standard/var_unserializer.c"
+#line 595 "ext/standard/var_unserializer.c"
 yy4:
        yych = *(YYMARKER = ++YYCURSOR);
        if (yych == ':') goto yy89;
@@ -633,13 +634,13 @@ yy13:
        goto yy3;
 yy14:
        ++YYCURSOR;
-#line 873 "ext/standard/var_unserializer.re"
+#line 874 "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 643 "ext/standard/var_unserializer.c"
+#line 644 "ext/standard/var_unserializer.c"
 yy16:
        yych = *++YYCURSOR;
        goto yy3;
@@ -669,7 +670,7 @@ yy20:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 728 "ext/standard/var_unserializer.re"
+#line 729 "ext/standard/var_unserializer.re"
        {
        size_t len, len2, len3, maxlen;
        zend_long elements;
@@ -814,7 +815,7 @@ yy20:
 
        return object_common2(UNSERIALIZE_PASSTHRU, elements);
 }
-#line 818 "ext/standard/var_unserializer.c"
+#line 819 "ext/standard/var_unserializer.c"
 yy25:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -839,7 +840,7 @@ yy27:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 720 "ext/standard/var_unserializer.re"
+#line 721 "ext/standard/var_unserializer.re"
        {
 
 //???  INIT_PZVAL(rval);
@@ -847,7 +848,7 @@ yy27:
        return object_common2(UNSERIALIZE_PASSTHRU,
                        object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
 }
-#line 851 "ext/standard/var_unserializer.c"
+#line 852 "ext/standard/var_unserializer.c"
 yy32:
        yych = *++YYCURSOR;
        if (yych == '+') goto yy33;
@@ -868,7 +869,7 @@ yy34:
        yych = *++YYCURSOR;
        if (yych != '{') goto yy18;
        ++YYCURSOR;
-#line 699 "ext/standard/var_unserializer.re"
+#line 700 "ext/standard/var_unserializer.re"
        {
        zend_long elements = parse_iv(start + 2);
        /* use iv() not uiv() in order to check data range */
@@ -889,7 +890,7 @@ yy34:
 
        return finish_nested_data(UNSERIALIZE_PASSTHRU);
 }
-#line 893 "ext/standard/var_unserializer.c"
+#line 894 "ext/standard/var_unserializer.c"
 yy39:
        yych = *++YYCURSOR;
        if (yych == '+') goto yy40;
@@ -910,7 +911,7 @@ yy41:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 671 "ext/standard/var_unserializer.re"
+#line 672 "ext/standard/var_unserializer.re"
        {
        size_t len, maxlen;
        zend_string *str;
@@ -938,7 +939,7 @@ yy41:
        ZVAL_STR(rval, str);
        return 1;
 }
-#line 942 "ext/standard/var_unserializer.c"
+#line 943 "ext/standard/var_unserializer.c"
 yy46:
        yych = *++YYCURSOR;
        if (yych == '+') goto yy47;
@@ -959,7 +960,7 @@ yy48:
        yych = *++YYCURSOR;
        if (yych != '"') goto yy18;
        ++YYCURSOR;
-#line 644 "ext/standard/var_unserializer.re"
+#line 645 "ext/standard/var_unserializer.re"
        {
        size_t len, maxlen;
        char *str;
@@ -986,7 +987,7 @@ yy48:
        ZVAL_STRINGL(rval, str, len);
        return 1;
 }
-#line 990 "ext/standard/var_unserializer.c"
+#line 991 "ext/standard/var_unserializer.c"
 yy53:
        yych = *++YYCURSOR;
        if (yych <= '/') {
@@ -1074,7 +1075,7 @@ yy61:
        }
 yy63:
        ++YYCURSOR;
-#line 635 "ext/standard/var_unserializer.re"
+#line 636 "ext/standard/var_unserializer.re"
        {
 #if SIZEOF_ZEND_LONG == 4
 use_double:
@@ -1083,7 +1084,7 @@ use_double:
        ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
        return 1;
 }
-#line 1087 "ext/standard/var_unserializer.c"
+#line 1088 "ext/standard/var_unserializer.c"
 yy65:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1142,7 +1143,7 @@ yy73:
        yych = *++YYCURSOR;
        if (yych != ';') goto yy18;
        ++YYCURSOR;
-#line 619 "ext/standard/var_unserializer.re"
+#line 620 "ext/standard/var_unserializer.re"
        {
        *p = YYCURSOR;
 
@@ -1158,7 +1159,7 @@ yy73:
 
        return 1;
 }
-#line 1162 "ext/standard/var_unserializer.c"
+#line 1163 "ext/standard/var_unserializer.c"
 yy76:
        yych = *++YYCURSOR;
        if (yych == 'N') goto yy73;
@@ -1185,7 +1186,7 @@ yy79:
        if (yych <= '9') goto yy79;
        if (yych != ';') goto yy18;
        ++YYCURSOR;
-#line 593 "ext/standard/var_unserializer.re"
+#line 594 "ext/standard/var_unserializer.re"
        {
 #if SIZEOF_ZEND_LONG == 4
        int digits = YYCURSOR - start - 3;
@@ -1211,7 +1212,7 @@ yy79:
        ZVAL_LONG(rval, parse_iv(start + 2));
        return 1;
 }
-#line 1215 "ext/standard/var_unserializer.c"
+#line 1216 "ext/standard/var_unserializer.c"
 yy83:
        yych = *++YYCURSOR;
        if (yych <= '/') goto yy18;
@@ -1219,22 +1220,22 @@ yy83:
        yych = *++YYCURSOR;
        if (yych != ';') goto yy18;
        ++YYCURSOR;
-#line 587 "ext/standard/var_unserializer.re"
+#line 588 "ext/standard/var_unserializer.re"
        {
        *p = YYCURSOR;
        ZVAL_BOOL(rval, parse_iv(start + 2));
        return 1;
 }
-#line 1229 "ext/standard/var_unserializer.c"
+#line 1230 "ext/standard/var_unserializer.c"
 yy87:
        ++YYCURSOR;
-#line 581 "ext/standard/var_unserializer.re"
+#line 582 "ext/standard/var_unserializer.re"
        {
        *p = YYCURSOR;
        ZVAL_NULL(rval);
        return 1;
 }
-#line 1238 "ext/standard/var_unserializer.c"
+#line 1239 "ext/standard/var_unserializer.c"
 yy89:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1257,7 +1258,7 @@ yy91:
        if (yych <= '9') goto yy91;
        if (yych != ';') goto yy18;
        ++YYCURSOR;
-#line 558 "ext/standard/var_unserializer.re"
+#line 559 "ext/standard/var_unserializer.re"
        {
        zend_long id;
 
@@ -1280,7 +1281,7 @@ yy91:
 
        return 1;
 }
-#line 1284 "ext/standard/var_unserializer.c"
+#line 1285 "ext/standard/var_unserializer.c"
 yy95:
        yych = *++YYCURSOR;
        if (yych <= ',') {
@@ -1303,7 +1304,7 @@ yy97:
        if (yych <= '9') goto yy97;
        if (yych != ';') goto yy18;
        ++YYCURSOR;
-#line 536 "ext/standard/var_unserializer.re"
+#line 537 "ext/standard/var_unserializer.re"
        {
        zend_long id;
 
@@ -1325,9 +1326,9 @@ yy97:
 
        return 1;
 }
-#line 1329 "ext/standard/var_unserializer.c"
+#line 1330 "ext/standard/var_unserializer.c"
 }
-#line 881 "ext/standard/var_unserializer.re"
+#line 882 "ext/standard/var_unserializer.re"
 
 
        return 0;
index 7d4ca2c803d4272014aaee2214532b37a4db82b0..dc2f84ac96af3454e0d6bb2a3604f1e5b8ddfec7 100644 (file)
@@ -474,13 +474,14 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
 {
        zval retval;
        zval fname;
+       HashTable *ht = Z_OBJPROP_P(rval);
 
        if (Z_TYPE_P(rval) != IS_OBJECT) {
                return 0;
        }
 
-       //??? TODO: resize before
-       if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
+       zend_hash_resize(ht, zend_hash_num_elements(ht) + elements);
+       if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
                return 0;
        }