diff options
author | Stanislav Malyshev <stas@php.net> | 2016-12-30 16:59:46 -0800 |
---|---|---|
committer | Stanislav Malyshev <stas@php.net> | 2016-12-30 16:59:46 -0800 |
commit | 16b3003ffc6393e250f069aa28a78dc5a2c064b2 (patch) | |
tree | 29261d5d2bcc06e22b18fc0c44bba184809deb37 | |
parent | b28b8b2fee6dfa6fcd13305c581bb835689ac3be (diff) | |
download | php-git-16b3003ffc6393e250f069aa28a78dc5a2c064b2.tar.gz |
Fix bug #73825 - Heap out of bounds read on unserialize in finish_nested_data()
-rw-r--r-- | ext/standard/tests/serialize/bug73825.phpt | 12 | ||||
-rw-r--r-- | ext/standard/var_unserializer.c | 80 | ||||
-rw-r--r-- | ext/standard/var_unserializer.re | 20 |
3 files changed, 76 insertions, 36 deletions
diff --git a/ext/standard/tests/serialize/bug73825.phpt b/ext/standard/tests/serialize/bug73825.phpt new file mode 100644 index 0000000000..adbfca1bbb --- /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 549149267e..efb0942b05 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 ce84bf5eb4..4cdf313735 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); } |