From ca46d0acbce55019b970fcd4c1e8a10edfdded93 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 30 Dec 2016 15:34:46 -0800 Subject: Fix int overflows in phar (bug #73764) --- ext/phar/phar.c | 4 ++-- ext/phar/tests/bug73764.phar | Bin 0 -> 138 bytes ext/phar/tests/bug73764.phpt | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 ext/phar/tests/bug73764.phar create mode 100644 ext/phar/tests/bug73764.phpt diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 14b80e175e..532b4c3169 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1055,7 +1055,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char entry.is_persistent = mydata->is_persistent; for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) { - if (buffer + 4 > endbuffer) { + if (buffer + 24 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)") } @@ -1069,7 +1069,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char entry.manifest_pos = manifest_index; } - if (entry.filename_len + 20 > endbuffer - buffer) { + if (entry.filename_len > endbuffer - buffer - 20) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); } diff --git a/ext/phar/tests/bug73764.phar b/ext/phar/tests/bug73764.phar new file mode 100644 index 0000000000..89a5ff6542 Binary files /dev/null and b/ext/phar/tests/bug73764.phar differ diff --git a/ext/phar/tests/bug73764.phpt b/ext/phar/tests/bug73764.phpt new file mode 100644 index 0000000000..cab314a731 --- /dev/null +++ b/ext/phar/tests/bug73764.phpt @@ -0,0 +1,16 @@ +--TEST-- +Phar: PHP bug #73764: Crash while loading hostile phar archive +--SKIPIF-- + +--FILE-- +getMessage(); +} +?> +--EXPECTF-- +internal corruption of phar "%sbug73764.phar" (truncated manifest entry) \ No newline at end of file -- cgit v1.2.1 From b28b8b2fee6dfa6fcd13305c581bb835689ac3be Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 30 Dec 2016 15:57:24 -0800 Subject: Fix bug #73768 - Memory corruption when loading hostile phar --- ext/phar/phar.c | 3 +-- ext/phar/tests/bug73768.phar | Bin 0 -> 219 bytes ext/phar/tests/bug73768.phpt | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 ext/phar/tests/bug73768.phar create mode 100644 ext/phar/tests/bug73768.phpt diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 532b4c3169..158f41739d 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -981,7 +981,6 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char /* if the alias is stored we enforce it (implicit overrides explicit) */ if (alias && alias_len && (alias_len != (int)tmp_len || strncmp(alias, buffer, tmp_len))) { - buffer[tmp_len] = '\0'; php_stream_close(fp); if (signature) { @@ -989,7 +988,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char } if (error) { - spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%s\" under different alias \"%s\"", fname, buffer, alias); + spprintf(error, 0, "cannot load phar \"%s\" with implicit alias \"%.*s\" under different alias \"%s\"", fname, tmp_len, buffer, alias); } efree(savebuf); diff --git a/ext/phar/tests/bug73768.phar b/ext/phar/tests/bug73768.phar new file mode 100644 index 0000000000..3f429c2365 Binary files /dev/null and b/ext/phar/tests/bug73768.phar differ diff --git a/ext/phar/tests/bug73768.phpt b/ext/phar/tests/bug73768.phpt new file mode 100644 index 0000000000..37a4da0253 --- /dev/null +++ b/ext/phar/tests/bug73768.phpt @@ -0,0 +1,16 @@ +--TEST-- +Phar: PHP bug #73768: Memory corruption when loading hostile phar +--SKIPIF-- + +--FILE-- +getMessage(); +} +?> +--EXPECTF-- +cannot load phar "%sbug73768.phar" with implicit alias "" under different alias "alias.phar" -- cgit v1.2.1 From 16b3003ffc6393e250f069aa28a78dc5a2c064b2 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 30 Dec 2016 16:59:46 -0800 Subject: Fix bug #73825 - Heap out of bounds read on unserialize in finish_nested_data() --- ext/standard/tests/serialize/bug73825.phpt | 12 +++++ ext/standard/var_unserializer.c | 80 ++++++++++++++++++------------ ext/standard/var_unserializer.re | 20 ++++++-- 3 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 ext/standard/tests/serialize/bug73825.phpt 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-- + +--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); } -- cgit v1.2.1 From e5246580a85f031e1a3b8064edbaa55c1643a451 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sat, 31 Dec 2016 18:47:50 -0800 Subject: Fix bug #73773 - Seg fault when loading hostile phar --- ext/phar/phar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 158f41739d..780be43257 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1054,7 +1054,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char entry.is_persistent = mydata->is_persistent; for (manifest_index = 0; manifest_index < manifest_count; ++manifest_index) { - if (buffer + 24 > endbuffer) { + if (buffer + 28 > endbuffer) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)") } @@ -1068,7 +1068,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char entry.manifest_pos = manifest_index; } - if (entry.filename_len > endbuffer - buffer - 20) { + if (entry.filename_len > endbuffer - buffer - 24) { MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest entry)"); } -- cgit v1.2.1 From 1cda0d7c2ffb62d8331c64e703131d9cabdc03ea Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sat, 31 Dec 2016 19:31:49 -0800 Subject: Fix bug #73737 FPE when parsing a tag format --- ext/exif/exif.c | 2 +- ext/exif/tests/bug73737.phpt | 12 ++++++++++++ ext/exif/tests/bug73737.tiff | Bin 0 -> 48 bytes 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 ext/exif/tests/bug73737.phpt create mode 100644 ext/exif/tests/bug73737.tiff diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 8b0e34c10d..83daee6f54 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -1303,7 +1303,7 @@ static size_t exif_convert_any_to_int(void *value, int format, int motorola_inte if (s_den == 0) { return 0; } else { - return php_ifd_get32s(value, motorola_intel) / s_den; + return (size_t)((double)php_ifd_get32s(value, motorola_intel) / s_den); } case TAG_FMT_SSHORT: return php_ifd_get16u(value, motorola_intel); diff --git a/ext/exif/tests/bug73737.phpt b/ext/exif/tests/bug73737.phpt new file mode 100644 index 0000000000..21eaf80585 --- /dev/null +++ b/ext/exif/tests/bug73737.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #73737 (Crash when parsing a tag format) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: exif_thumbnail(bug73737.tiff): Error in TIFF: filesize(x0030) less than start of IFD dir(x10102) in %s line %d +bool(false) diff --git a/ext/exif/tests/bug73737.tiff b/ext/exif/tests/bug73737.tiff new file mode 100644 index 0000000000..2cb036fc47 Binary files /dev/null and b/ext/exif/tests/bug73737.tiff differ -- cgit v1.2.1