Skip to content

Commit 48f0f73

Browse files
committed
Fix bug #77143 - add more checks to buffer reads
1 parent 7edc639 commit 48f0f73

File tree

5 files changed

+42
-11
lines changed

5 files changed

+42
-11
lines changed

NEWS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ PHP NEWS
99

1010
- Phar:
1111
. Fixed bug #77022 (PharData always creates new files with mode 0666). (Stas)
12-
12+
. Fixed bug #77143 (Heap Buffer Overflow (READ: 4) in phar_parse_pharfile).
13+
(Stas)
1314

1415
13 Sep 2018, PHP 5.6.38
1516

ext/phar/phar.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,18 @@ int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_
638638
}
639639
/* }}}*/
640640

641+
/**
642+
* Size of fixed fields in the manifest.
643+
* See: http://php.net/manual/en/phar.fileformat.phar.php
644+
*/
645+
#define MANIFEST_FIXED_LEN 18
646+
647+
#define SAFE_PHAR_GET_32(buffer, endbuffer, var) \
648+
if (UNEXPECTED(buffer + 4 > endbuffer)) { \
649+
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)"); \
650+
} \
651+
PHAR_GET_32(buffer, var);
652+
641653
/**
642654
* Does not check for a previously opened phar in the cache.
643655
*
@@ -721,12 +733,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
721733
savebuf = buffer;
722734
endbuffer = buffer + manifest_len;
723735

724-
if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
736+
if (manifest_len < MANIFEST_FIXED_LEN || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
725737
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
726738
}
727739

728740
/* extract the number of entries */
729-
PHAR_GET_32(buffer, manifest_count);
741+
SAFE_PHAR_GET_32(buffer, endbuffer, manifest_count);
730742

731743
if (manifest_count == 0) {
732744
MAPPHAR_FAIL("in phar \"%s\", manifest claims to have zero entries. Phars must have at least 1 entry");
@@ -746,7 +758,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
746758
return FAILURE;
747759
}
748760

749-
PHAR_GET_32(buffer, manifest_flags);
761+
SAFE_PHAR_GET_32(buffer, endbuffer, manifest_flags);
750762

751763
manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
752764
manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
@@ -966,13 +978,13 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
966978
}
967979

968980
/* extract alias */
969-
PHAR_GET_32(buffer, tmp_len);
981+
SAFE_PHAR_GET_32(buffer, endbuffer, tmp_len);
970982

971983
if (buffer + tmp_len > endbuffer) {
972984
MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
973985
}
974986

975-
if (manifest_len < 10 + tmp_len) {
987+
if (manifest_len < MANIFEST_FIXED_LEN + tmp_len) {
976988
MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
977989
}
978990

@@ -1010,7 +1022,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
10101022
}
10111023

10121024
/* we have 5 32-bit items plus 1 byte at least */
1013-
if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
1025+
if (manifest_count > ((manifest_len - MANIFEST_FIXED_LEN - tmp_len) / (5 * 4 + 1))) {
10141026
/* prevent serious memory issues */
10151027
MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
10161028
}
@@ -1019,12 +1031,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
10191031
mydata->is_persistent = PHAR_G(persist);
10201032

10211033
/* check whether we have meta data, zero check works regardless of byte order */
1022-
PHAR_GET_32(buffer, len);
1034+
SAFE_PHAR_GET_32(buffer, endbuffer, len);
10231035
if (mydata->is_persistent) {
10241036
mydata->metadata_len = len;
1025-
if(!len) {
1037+
if (!len) {
10261038
/* FIXME: not sure why this is needed but removing it breaks tests */
1027-
PHAR_GET_32(buffer, len);
1039+
SAFE_PHAR_GET_32(buffer, endbuffer, len);
10281040
}
10291041
}
10301042
if(len > endbuffer - buffer) {

ext/phar/tests/bug73768.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ echo "OK\n";
1313
}
1414
?>
1515
--EXPECTF--
16-
cannot load phar "%sbug73768.phar" with implicit alias "" under different alias "alias.phar"
16+
internal corruption of phar "%sbug73768.phar" (truncated manifest header)

ext/phar/tests/bug77143.phar

50 Bytes
Binary file not shown.

ext/phar/tests/bug77143.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
PHP bug #77143: Heap Buffer Overflow (READ: 4) in phar_parse_pharfile
3+
--INI--
4+
phar.readonly=0
5+
--SKIPIF--
6+
<?php if (!extension_loaded("phar")) die("skip"); ?>
7+
--FILE--
8+
<?php
9+
chdir(__DIR__);
10+
try {
11+
var_dump(new Phar('bug77143.phar',0,'project.phar'));
12+
echo "OK\n";
13+
} catch(UnexpectedValueException $e) {
14+
echo $e->getMessage();
15+
}
16+
?>
17+
--EXPECTF--
18+
internal corruption of phar "%sbug77143.phar" (truncated manifest header)

0 commit comments

Comments
 (0)