]> granicus.if.org Git - php/commitdiff
Fix bug #77143 - add more checks to buffer reads
authorStanislav Malyshev <stas@php.net>
Mon, 12 Nov 2018 22:02:26 +0000 (14:02 -0800)
committerStanislav Malyshev <stas@php.net>
Mon, 3 Dec 2018 08:41:46 +0000 (00:41 -0800)
NEWS
ext/phar/phar.c
ext/phar/tests/bug73768.phpt
ext/phar/tests/bug77143.phar [new file with mode: 0644]
ext/phar/tests/bug77143.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 34ff5848d57ad13b1964c0f807c7e28bdbd5bad0..727e874f97c1fad7072d701db94f4b347c81a107 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,7 +9,8 @@ PHP                                                                        NEWS
 
 - Phar:
   . Fixed bug #77022 (PharData always creates new files with mode 0666). (Stas)
-
+  . Fixed bug #77143 (Heap Buffer Overflow (READ: 4) in phar_parse_pharfile).
+    (Stas)
 
 13 Sep 2018, PHP 5.6.38
 
index 780be432570e80dd34c1a9c217ef87ade22bf136..47ff8cd790730dbc3520e47faa86327e2987a238 100644 (file)
@@ -638,6 +638,18 @@ int phar_parse_metadata(char **buffer, zval **metadata, php_uint32 zip_metadata_
 }
 /* }}}*/
 
+/**
+ * Size of fixed fields in the manifest.
+ * See: http://php.net/manual/en/phar.fileformat.phar.php
+ */
+#define MANIFEST_FIXED_LEN     18
+
+#define SAFE_PHAR_GET_32(buffer, endbuffer, var) \
+       if (UNEXPECTED(buffer + 4 > endbuffer)) { \
+               MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)"); \
+       } \
+       PHAR_GET_32(buffer, var);
+
 /**
  * Does not check for a previously opened phar in the cache.
  *
@@ -721,12 +733,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        savebuf = buffer;
        endbuffer = buffer + manifest_len;
 
-       if (manifest_len < 10 || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
+       if (manifest_len < MANIFEST_FIXED_LEN || manifest_len != php_stream_read(fp, buffer, manifest_len)) {
                MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
        }
 
        /* extract the number of entries */
-       PHAR_GET_32(buffer, manifest_count);
+       SAFE_PHAR_GET_32(buffer, endbuffer, manifest_count);
 
        if (manifest_count == 0) {
                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
                return FAILURE;
        }
 
-       PHAR_GET_32(buffer, manifest_flags);
+       SAFE_PHAR_GET_32(buffer, endbuffer, manifest_flags);
 
        manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK;
        manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK;
@@ -966,13 +978,13 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        }
 
        /* extract alias */
-       PHAR_GET_32(buffer, tmp_len);
+       SAFE_PHAR_GET_32(buffer, endbuffer, tmp_len);
 
        if (buffer + tmp_len > endbuffer) {
                MAPPHAR_FAIL("internal corruption of phar \"%s\" (buffer overrun)");
        }
 
-       if (manifest_len < 10 + tmp_len) {
+       if (manifest_len < MANIFEST_FIXED_LEN + tmp_len) {
                MAPPHAR_FAIL("internal corruption of phar \"%s\" (truncated manifest header)")
        }
 
@@ -1010,7 +1022,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        }
 
        /* we have 5 32-bit items plus 1 byte at least */
-       if (manifest_count > ((manifest_len - 10 - tmp_len) / (5 * 4 + 1))) {
+       if (manifest_count > ((manifest_len - MANIFEST_FIXED_LEN - tmp_len) / (5 * 4 + 1))) {
                /* prevent serious memory issues */
                MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
        }
@@ -1019,12 +1031,12 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
        mydata->is_persistent = PHAR_G(persist);
 
        /* check whether we have meta data, zero check works regardless of byte order */
-       PHAR_GET_32(buffer, len);
+       SAFE_PHAR_GET_32(buffer, endbuffer, len);
        if (mydata->is_persistent) {
                mydata->metadata_len = len;
-               if(!len) {
+               if (!len) {
                        /* FIXME: not sure why this is needed but removing it breaks tests */
-                       PHAR_GET_32(buffer, len);
+                       SAFE_PHAR_GET_32(buffer, endbuffer, len);
                }
        }
        if(len > endbuffer - buffer) {
index 37a4da0253acf76e767ccb3192c730c769431fe2..3062268b80db036565ac27a3493115c6b20f7a8a 100644 (file)
@@ -13,4 +13,4 @@ echo "OK\n";
 }
 ?>
 --EXPECTF--
-cannot load phar "%sbug73768.phar" with implicit alias "" under different alias "alias.phar"
+internal corruption of phar "%sbug73768.phar" (truncated manifest header)
diff --git a/ext/phar/tests/bug77143.phar b/ext/phar/tests/bug77143.phar
new file mode 100644 (file)
index 0000000..eb797b5
Binary files /dev/null and b/ext/phar/tests/bug77143.phar differ
diff --git a/ext/phar/tests/bug77143.phpt b/ext/phar/tests/bug77143.phpt
new file mode 100644 (file)
index 0000000..f9f80fc
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+PHP bug #77143: Heap Buffer Overflow (READ: 4) in phar_parse_pharfile
+--INI--
+phar.readonly=0
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--FILE--
+<?php
+chdir(__DIR__);
+try {
+var_dump(new Phar('bug77143.phar',0,'project.phar'));
+echo "OK\n";
+} catch(UnexpectedValueException $e) {
+       echo $e->getMessage();
+}
+?>
+--EXPECTF--
+internal corruption of phar "%sbug77143.phar" (truncated manifest header)