Problem/Motivation

Drupal core - Critical - Third Party Libraries - SA-CORE-2019-001 is throwing the below fatal error when calling \Drupal\Core\Archiver\ArchiveTar::addModify

Warning: pack(): Type a: not enough arguments in Drupal\Core\Archiver\ArchiveTar->_writeHeader() (line 1475 of core/lib/Drupal/Core/Archiver/ArchiveTar.php).

I was able to pinpoint the missing pack() argument and core/lib/Drupal/Core/Archiver/ArchiveTar.php LINE 1469 is missing the $v_reduced_filename argument

The below code…

$v_binary_data_first = pack(
  "a100a8a8a8a12a12",
  $v_perms,
  $v_uid,
  $v_gid,
  $v_size,
  $v_mtime
);

…needs to include the $v_reduced_filename parameter…

$v_binary_data_first = pack(
  "a100a8a8a8a12a12",
  $v_reduced_filename,
  $v_perms,
  $v_uid,
  $v_gid,
  $v_size,
  $v_mtime
);

For reference here is the original code from PEAR Archive_Tar.
https://github.com/pear/Archive_Tar/blob/master/Archive/Tar.php#L1510

I discovered this typo because Webform is using ArchiveTar to compress exported files.
@see #3026465: Submission export with compressed archive is no longer working

Proposed resolution

Add missing $v_reduced_filename argument to \Drupal\Core\Archiver\ArchiveTar::_writeHeader

Remaining tasks

Determine if fixing the typo is acceptable
Determine if we need to write test coverage. (NOTE: Archive_Tar has test coverage)
Backport to 8.5, and 8.6.
Decide when this fix can be released.

User interface changes

N/A

API changes

N/A

Data model changes

N/A

Release notes snippet

N/A

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jrockowitz created an issue. See original summary.

jrockowitz’s picture

Status: Active » Needs review
FileSize
499 bytes
pandaski’s picture

Version: 8.6.x-dev » 8.7.x-dev

Verified the same issue as 8.7.x

pandaski’s picture

Status: Needs review » Reviewed & tested by the community

Reference: https://api.drupal.org/api/drupal/modules%21system%21system.tar.inc/func...

$v_binary_data_first = pack("a100a8a8a8a12a12", $v_reduced_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
xjm’s picture

Status: Reviewed & tested by the community » Needs review

Thanks for the great issue report! I am honestly not sure how that even happened.

We should probably check a diff of the whole commit to ensure it's identical to the upstream library -- if this hunk is messed up, maybe others are as well. Also, we should check our test coverage; I thought we had our own test coverage for the library. Setting NR for us to look into those two things before we commit.

alexpott’s picture

This is why we need an issue to move this dependency into composer and out of pseudo-dependency land.

alexpott’s picture

alexpott’s picture

So we have to match the diff of git diff b3aef2bced...HEAD core/lib/Drupal/Core/Archiver/ArchiveTar.php where HEAD is a branch with this patch applied to https://github.com/pear/Archive_Tar/compare/1.4.0...1.4.5

Here's our diff

diff --git a/core/lib/Drupal/Core/Archiver/ArchiveTar.php b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
index 716511edee..90dd57386b 100644
--- a/core/lib/Drupal/Core/Archiver/ArchiveTar.php
+++ b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
@@ -42,7 +42,7 @@
 
  /**
  * Note on Drupal 8 porting.
- * This file origin is Tar.php, release 1.4.0 (stable) with some code
+ * This file origin is Tar.php, release 1.4.5 (stable) with some code
  * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
  * To simplify future porting from pear of this file, you should not
  * do cosmetic or other non significant changes to this file.
@@ -151,6 +151,13 @@ class ArchiveTar
      */
     public $error_object = null;
 
+    /**
+     * Format for data extraction
+     *
+     * @var string
+     */
+    public $_fmt ='';
+
     /**
      * Archive_Tar Class constructor. This flavour of the constructor only
      * declare a new Archive_Tar object, identifying it by the name of the
@@ -257,6 +264,18 @@ public function __construct($p_tarname, $p_compress = null)
                 return false;
             }
         }
+
+        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
+            $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
+                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
+                   "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
+        } else {
+            $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
+                   "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
+                   "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
+        }
+
+
     }
 
     public function __destruct()
@@ -712,7 +731,7 @@ public function setAttribute()
         }
 
         // ----- Get the arguments
-        $v_att_list = & func_get_args();
+        $v_att_list = func_get_args();
 
         // ----- Read the attributes
         $i = 0;
@@ -1392,10 +1411,20 @@ public function _writeHeader($p_filename, $p_stored_filename)
         if ($p_stored_filename == '') {
             $p_stored_filename = $p_filename;
         }
-        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
+        $v_reduced_filename = $this->_pathReduction($p_stored_filename);
 
-        if (strlen($v_reduce_filename) > 99) {
-            if (!$this->_writeLongHeader($v_reduce_filename)) {
+        if (strlen($v_reduced_filename) > 99) {
+            if (!$this->_writeLongHeader($v_reduced_filename, false)) {
+                return false;
+            }
+        }
+
+        $v_linkname = '';
+        if (@is_link($p_filename)) {
+            $v_linkname = readlink($p_filename);
+        }
+        if (strlen($v_linkname) > 99) {
+            if (!$this->_writeLongHeader($v_linkname, true)) {
                 return false;
             }
         }
@@ -1404,14 +1433,10 @@ public function _writeHeader($p_filename, $p_stored_filename)
         $v_uid = sprintf("%07s", DecOct($v_info[4]));
         $v_gid = sprintf("%07s", DecOct($v_info[5]));
         $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
-
         $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
 
-        $v_linkname = '';
-
         if (@is_link($p_filename)) {
             $v_typeflag = '2';
-            $v_linkname = readlink($p_filename);
             $v_size = sprintf("%011s", DecOct(0));
         } elseif (@is_dir($p_filename)) {
             $v_typeflag = "5";
@@ -1423,7 +1448,6 @@ public function _writeHeader($p_filename, $p_stored_filename)
         }
 
         $v_magic = 'ustar ';
-
         $v_version = ' ';
 
         if (function_exists('posix_getpwuid')) {
@@ -1438,14 +1462,13 @@ public function _writeHeader($p_filename, $p_stored_filename)
         }
 
         $v_devmajor = '';
-
         $v_devminor = '';
 
         $v_prefix = '';
 
         $v_binary_data_first = pack(
             "a100a8a8a8a12a12",
-            $v_reduce_filename,
+            $v_reduced_filename,
             $v_perms,
             $v_uid,
             $v_gid,
@@ -1485,7 +1508,7 @@ public function _writeHeader($p_filename, $p_stored_filename)
         $this->_writeBlock($v_binary_data_first, 148);
 
         // ----- Write the calculated checksum
-        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
         $v_binary_data = pack("a8", $v_checksum);
         $this->_writeBlock($v_binary_data, 8);
 
@@ -1517,7 +1540,7 @@ public function _writeHeaderBlock(
         $p_filename = $this->_pathReduction($p_filename);
 
         if (strlen($p_filename) > 99) {
-            if (!$this->_writeLongHeader($p_filename)) {
+            if (!$this->_writeLongHeader($p_filename, false)) {
                 return false;
             }
         }
@@ -1613,36 +1636,31 @@ public function _writeHeaderBlock(
      * @param string $p_filename
      * @return bool
      */
-    public function _writeLongHeader($p_filename)
+    public function _writeLongHeader($p_filename, $is_link = false)
     {
-        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
-
-        $v_typeflag = 'L';
-
+        $v_uid = sprintf("%07s", 0);
+        $v_gid = sprintf("%07s", 0);
+        $v_perms = sprintf("%07s", 0);
+        $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
+        $v_mtime = sprintf("%011s", 0);
+        $v_typeflag = ($is_link ? 'K' : 'L');
         $v_linkname = '';
-
-        $v_magic = '';
-
-        $v_version = '';
-
+        $v_magic = 'ustar ';
+        $v_version = ' ';
         $v_uname = '';
-
         $v_gname = '';
-
         $v_devmajor = '';
-
         $v_devminor = '';
-
         $v_prefix = '';
 
         $v_binary_data_first = pack(
             "a100a8a8a8a12a12",
             '././@LongLink',
-            0,
-            0,
-            0,
+            $v_perms,
+            $v_uid,
+            $v_gid,
             $v_size,
-            0
+            $v_mtime
         );
         $v_binary_data_last = pack(
             "a1a100a6a2a32a32a8a8a155a12",
@@ -1677,7 +1695,7 @@ public function _writeLongHeader($p_filename)
         $this->_writeBlock($v_binary_data_first, 148);
 
         // ----- Write the calculated checksum
-        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
         $v_binary_data = pack("a8", $v_checksum);
         $this->_writeBlock($v_binary_data, 8);
 
@@ -1718,28 +1736,12 @@ public function _readHeader($v_binary_data, &$v_header)
         // ----- Calculate the checksum
         $v_checksum = 0;
         // ..... First part of the header
-        for ($i = 0; $i < 148; $i++) {
-            $v_checksum += ord(substr($v_binary_data, $i, 1));
-        }
-        // ..... Ignore the checksum value and replace it by ' ' (space)
-        for ($i = 148; $i < 156; $i++) {
-            $v_checksum += ord(' ');
-        }
-        // ..... Last part of the header
-        for ($i = 156; $i < 512; $i++) {
-            $v_checksum += ord(substr($v_binary_data, $i, 1));
-        }
+        $v_binary_split = str_split($v_binary_data);
+        $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
+        $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
+        $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
 
-        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
-            $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
-                "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
-                "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
-        } else {
-            $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
-                "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
-                "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
-        }
-        $v_data = unpack($fmt, $v_binary_data);
+        $v_data = unpack($this->_fmt, $v_binary_data);
 
         if (strlen($v_data["prefix"]) > 0) {
             $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
@@ -1775,7 +1777,7 @@ public function _readHeader($v_binary_data, &$v_header)
         $v_header['mode'] = OctDec(trim($v_data['mode']));
         $v_header['uid'] = OctDec(trim($v_data['uid']));
         $v_header['gid'] = OctDec(trim($v_data['gid']));
-        $v_header['size'] = OctDec(trim($v_data['size']));
+        $v_header['size'] = $this->_tarRecToSize($v_data['size']);
         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
             $v_header['size'] = 0;
@@ -1794,6 +1796,41 @@ public function _readHeader($v_binary_data, &$v_header)
         return true;
     }
 
+    /**
+     * Convert Tar record size to actual size
+     *
+     * @param string $tar_size
+     * @return size of tar record in bytes
+     */
+    private function _tarRecToSize($tar_size)
+    {
+        /*
+         * First byte of size has a special meaning if bit 7 is set.
+         *
+         * Bit 7 indicates base-256 encoding if set.
+         * Bit 6 is the sign bit.
+         * Bits 5:0 are most significant value bits.
+         */
+        $ch = ord($tar_size[0]);
+        if ($ch & 0x80) {
+            // Full 12-bytes record is required.
+            $rec_str = $tar_size . "\x00";
+
+            $size = ($ch & 0x40) ? -1 : 0;
+            $size = ($size << 6) | ($ch & 0x3f);
+
+            for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
+                $size = ($size * 256) + ord($rec_str[$num_ch]);
+            }
+
+            return $size;
+
+        } else {
+            return OctDec(trim($tar_size));
+        }
+    }
+
+
     /**
      * Detect and report a malicious file name
      *
@@ -1803,10 +1840,13 @@ public function _readHeader($v_binary_data, &$v_header)
      */
     private function _maliciousFilename($file)
     {
-        if (strpos($file, '/../') !== false) {
+        if (strpos($file, 'phar://') === 0) {
             return true;
         }
-        if (strpos($file, '../') === 0) {
+        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
+            return true;
+        }
+        if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
             return true;
         }
         return false;
@@ -1871,11 +1911,20 @@ private function _extractInString($p_filename)
                 continue;
             }
 
-            // ----- Look for long filename
-            if ($v_header['typeflag'] == 'L') {
-                if (!$this->_readLongHeader($v_header)) {
-                    return null;
-                }
+            switch ($v_header['typeflag']) {
+                case 'L': {
+                    if (!$this->_readLongHeader($v_header)) {
+                        return null;
+                    }
+                } break;
+
+                case 'K': {
+                    $v_link_header = $v_header;
+                    if (!$this->_readLongHeader($v_link_header)) {
+                        return null;
+                    }
+                    $v_header['link'] = $v_link_header['filename'];
+                } break;
             }
 
             if ($v_header['filename'] == $p_filename) {
@@ -1976,11 +2025,20 @@ public function _extractList(
                 continue;
             }
 
-            // ----- Look for long filename
-            if ($v_header['typeflag'] == 'L') {
-                if (!$this->_readLongHeader($v_header)) {
-                    return false;
-                }
+            switch ($v_header['typeflag']) {
+                case 'L': {
+                    if (!$this->_readLongHeader($v_header)) {
+                        return null;
+                    }
+                } break;
+
+                case 'K': {
+                    $v_link_header = $v_header;
+                    if (!$this->_readLongHeader($v_link_header)) {
+                        return null;
+                    }
+                    $v_header['link'] = $v_link_header['filename'];
+                } break;
             }
 
             // ignore extended / pax headers

and here is the pear diff git diff 1.4.0...1.4.5 Archive/Tar.php

diff --git a/Archive/Tar.php b/Archive/Tar.php
index 07550e6..68bdffe 100644
--- a/Archive/Tar.php
+++ b/Archive/Tar.php
@@ -39,7 +39,11 @@
  * @link      http://pear.php.net/package/Archive_Tar
  */
 
-require_once 'PEAR.php';
+// If the PEAR class cannot be loaded via the autoloader,
+// then try to require_once it from the PHP include path.
+if (!class_exists('PEAR')) {
+    require_once 'PEAR.php';
+}
 
 define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
 define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
@@ -115,6 +119,12 @@ class Archive_Tar extends PEAR
      */
     public $error_object = null;
 
+    /**
+     * Format for data extraction
+     *
+     * @var string
+     */
+    public $_fmt ='';
     /**
      * Archive_Tar Class constructor. This flavour of the constructor only
      * declare a new Archive_Tar object, identifying it by the name of the
@@ -133,7 +143,7 @@ class Archive_Tar extends PEAR
     public function __construct($p_tarname, $p_compress = null)
     {
         parent::__construct();
-        
+
         $this->_compress = false;
         $this->_compress_type = 'none';
         if (($p_compress === null) || ($p_compress == '')) {
@@ -220,6 +230,19 @@ class Archive_Tar extends PEAR
                 return false;
             }
         }
+
+
+        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
+            $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
+                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
+                   "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
+        } else {
+            $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
+                   "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
+                   "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
+        }
+
+
     }
 
     public function __destruct()
@@ -636,7 +659,7 @@ class Archive_Tar extends PEAR
         }
 
         // ----- Get the arguments
-        $v_att_list = & func_get_args();
+        $v_att_list = func_get_args();
 
         // ----- Read the attributes
         $i = 0;
@@ -1314,10 +1337,22 @@ class Archive_Tar extends PEAR
         if ($p_stored_filename == '') {
             $p_stored_filename = $p_filename;
         }
-        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
 
-        if (strlen($v_reduce_filename) > 99) {
-            if (!$this->_writeLongHeader($v_reduce_filename)) {
+        $v_reduced_filename = $this->_pathReduction($p_stored_filename);
+
+        if (strlen($v_reduced_filename) > 99) {
+            if (!$this->_writeLongHeader($v_reduced_filename, false)) {
+                return false;
+            }
+        }
+
+        $v_linkname = '';
+        if (@is_link($p_filename)) {
+            $v_linkname = readlink($p_filename);
+        }
+
+        if (strlen($v_linkname) > 99) {
+            if (!$this->_writeLongHeader($v_linkname, true)) {
                 return false;
             }
         }
@@ -1326,14 +1361,10 @@ class Archive_Tar extends PEAR
         $v_uid = sprintf("%07s", DecOct($v_info[4]));
         $v_gid = sprintf("%07s", DecOct($v_info[5]));
         $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
-
         $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
 
-        $v_linkname = '';
-
         if (@is_link($p_filename)) {
             $v_typeflag = '2';
-            $v_linkname = readlink($p_filename);
             $v_size = sprintf("%011s", DecOct(0));
         } elseif (@is_dir($p_filename)) {
             $v_typeflag = "5";
@@ -1345,7 +1376,6 @@ class Archive_Tar extends PEAR
         }
 
         $v_magic = 'ustar ';
-
         $v_version = ' ';
 
         if (function_exists('posix_getpwuid')) {
@@ -1360,14 +1390,12 @@ class Archive_Tar extends PEAR
         }
 
         $v_devmajor = '';
-
         $v_devminor = '';
-
         $v_prefix = '';
 
         $v_binary_data_first = pack(
             "a100a8a8a8a12a12",
-            $v_reduce_filename,
+            $v_reduced_filename,
             $v_perms,
             $v_uid,
             $v_gid,
@@ -1407,7 +1435,7 @@ class Archive_Tar extends PEAR
         $this->_writeBlock($v_binary_data_first, 148);
 
         // ----- Write the calculated checksum
-        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
         $v_binary_data = pack("a8", $v_checksum);
         $this->_writeBlock($v_binary_data, 8);
 
@@ -1439,7 +1467,7 @@ class Archive_Tar extends PEAR
         $p_filename = $this->_pathReduction($p_filename);
 
         if (strlen($p_filename) > 99) {
-            if (!$this->_writeLongHeader($p_filename)) {
+            if (!$this->_writeLongHeader($p_filename, false)) {
                 return false;
             }
         }
@@ -1535,36 +1563,31 @@ class Archive_Tar extends PEAR
      * @param string $p_filename
      * @return bool
      */
-    public function _writeLongHeader($p_filename)
+    public function _writeLongHeader($p_filename, $is_link = false)
     {
-        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
-
-        $v_typeflag = 'L';
-
+        $v_uid = sprintf("%07s", 0);
+        $v_gid = sprintf("%07s", 0);
+        $v_perms = sprintf("%07s", 0);
+        $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
+        $v_mtime = sprintf("%011s", 0);
+        $v_typeflag = ($is_link ? 'K' : 'L');
         $v_linkname = '';
-
-        $v_magic = '';
-
-        $v_version = '';
-
+        $v_magic = 'ustar ';
+        $v_version = ' ';
         $v_uname = '';
-
         $v_gname = '';
-
         $v_devmajor = '';
-
         $v_devminor = '';
-
         $v_prefix = '';
 
         $v_binary_data_first = pack(
             "a100a8a8a8a12a12",
             '././@LongLink',
-            0,
-            0,
-            0,
+            $v_perms,
+            $v_uid,
+            $v_gid,
             $v_size,
-            0
+            $v_mtime
         );
         $v_binary_data_last = pack(
             "a1a100a6a2a32a32a8a8a155a12",
@@ -1599,7 +1622,7 @@ class Archive_Tar extends PEAR
         $this->_writeBlock($v_binary_data_first, 148);
 
         // ----- Write the calculated checksum
-        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
         $v_binary_data = pack("a8", $v_checksum);
         $this->_writeBlock($v_binary_data, 8);
 
@@ -1640,28 +1663,13 @@ class Archive_Tar extends PEAR
         // ----- Calculate the checksum
         $v_checksum = 0;
         // ..... First part of the header
-        for ($i = 0; $i < 148; $i++) {
-            $v_checksum += ord(substr($v_binary_data, $i, 1));
-        }
-        // ..... Ignore the checksum value and replace it by ' ' (space)
-        for ($i = 148; $i < 156; $i++) {
-            $v_checksum += ord(' ');
-        }
-        // ..... Last part of the header
-        for ($i = 156; $i < 512; $i++) {
-            $v_checksum += ord(substr($v_binary_data, $i, 1));
-        }
+        $v_binary_split = str_split($v_binary_data);
+        $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
+        $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
+        $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
 
-        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
-            $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
-                "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
-                "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
-        } else {
-            $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
-                "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
-                "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
-        }
-        $v_data = unpack($fmt, $v_binary_data);
+
+        $v_data = unpack($this->_fmt, $v_binary_data);
 
         if (strlen($v_data["prefix"]) > 0) {
             $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
@@ -1697,7 +1705,7 @@ class Archive_Tar extends PEAR
         $v_header['mode'] = OctDec(trim($v_data['mode']));
         $v_header['uid'] = OctDec(trim($v_data['uid']));
         $v_header['gid'] = OctDec(trim($v_data['gid']));
-        $v_header['size'] = OctDec(trim($v_data['size']));
+        $v_header['size'] = $this->_tarRecToSize($v_data['size']);
         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
             $v_header['size'] = 0;
@@ -1716,6 +1724,40 @@ class Archive_Tar extends PEAR
         return true;
     }
 
+    /**
+     * Convert Tar record size to actual size
+     *
+     * @param string $tar_size
+     * @return size of tar record in bytes
+     */
+    private function _tarRecToSize($tar_size)
+    {
+        /*
+         * First byte of size has a special meaning if bit 7 is set.
+         *
+         * Bit 7 indicates base-256 encoding if set.
+         * Bit 6 is the sign bit.
+         * Bits 5:0 are most significant value bits.
+         */
+        $ch = ord($tar_size[0]);
+        if ($ch & 0x80) {
+            // Full 12-bytes record is required.
+            $rec_str = $tar_size . "\x00";
+
+            $size = ($ch & 0x40) ? -1 : 0;
+            $size = ($size << 6) | ($ch & 0x3f);
+
+            for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
+                $size = ($size * 256) + ord($rec_str[$num_ch]);
+            }
+
+            return $size;
+
+        } else {
+            return OctDec(trim($tar_size));
+        }
+    }
+
     /**
      * Detect and report a malicious file name
      *
@@ -1725,10 +1767,13 @@ class Archive_Tar extends PEAR
      */
     private function _maliciousFilename($file)
     {
-        if (strpos($file, '/../') !== false) {
+        if (strpos($file, 'phar://') === 0) {
+            return true;
+        }
+        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
             return true;
         }
-        if (strpos($file, '../') === 0) {
+        if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
             return true;
         }
         return false;
@@ -1793,11 +1838,20 @@ class Archive_Tar extends PEAR
                 continue;
             }
 
-            // ----- Look for long filename
-            if ($v_header['typeflag'] == 'L') {
-                if (!$this->_readLongHeader($v_header)) {
-                    return null;
-                }
+            switch ($v_header['typeflag']) {
+                case 'L': {
+                    if (!$this->_readLongHeader($v_header)) {
+                        return null;
+                    }
+                } break;
+
+                case 'K': {
+                    $v_link_header = $v_header;
+                    if (!$this->_readLongHeader($v_link_header)) {
+                        return null;
+                    }
+                    $v_header['link'] = $v_link_header['filename'];
+                } break;
             }
 
             if ($v_header['filename'] == $p_filename) {
@@ -1898,11 +1952,20 @@ class Archive_Tar extends PEAR
                 continue;
             }
 
-            // ----- Look for long filename
-            if ($v_header['typeflag'] == 'L') {
-                if (!$this->_readLongHeader($v_header)) {
-                    return false;
-                }
+            switch ($v_header['typeflag']) {
+                case 'L': {
+                    if (!$this->_readLongHeader($v_header)) {
+                        return null;
+                    }
+                } break;
+
+                case 'K': {
+                    $v_link_header = $v_header;
+                    if (!$this->_readLongHeader($v_link_header)) {
+                        return null;
+                    }
+                    $v_header['link'] = $v_link_header['filename'];
+                } break;
             }
 
             // ignore extended / pax headers

xjm’s picture

#8 isn't going to help in that form; I think we need to diff the diffs.

alexpott’s picture

Doing a diff of those two diff using git diff ignore whitespace feature gives us

diff --git a/Users/alex/dev/drupal.diff b/Users/alex/dev/pear.diff
index 69f421f110..93b05d82a6 100644
--- a/Users/alex/dev/drupal.diff
+++ b/Users/alex/dev/pear.diff
@@ -1,17 +1,21 @@
-diff --git a/core/lib/Drupal/Core/Archiver/ArchiveTar.php b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-index 716511edee..90dd57386b 100644
---- a/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-+++ b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-@@ -42,7 +42,7 @@
+diff --git a/Archive/Tar.php b/Archive/Tar.php
+index 07550e6..68bdffe 100644
+--- a/Archive/Tar.php
++++ b/Archive/Tar.php
+@@ -39,7 +39,11 @@
+  * @link      http://pear.php.net/package/Archive_Tar
+  */
  
-  /**
-  * Note on Drupal 8 porting.
-- * This file origin is Tar.php, release 1.4.0 (stable) with some code
-+ * This file origin is Tar.php, release 1.4.5 (stable) with some code
-  * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
-  * To simplify future porting from pear of this file, you should not
-  * do cosmetic or other non significant changes to this file.
-@@ -151,6 +151,13 @@ class ArchiveTar
+-require_once 'PEAR.php';
++// If the PEAR class cannot be loaded via the autoloader,
++// then try to require_once it from the PHP include path.
++if (!class_exists('PEAR')) {
++    require_once 'PEAR.php';
++}
+ 
+ define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+ define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
+@@ -115,6 +119,12 @@ class Archive_Tar extends PEAR
       */
      public $error_object = null;
  
@@ -21,15 +25,24 @@ index 716511edee..90dd57386b 100644
 +     * @var string
 +     */
 +    public $_fmt ='';
-+
      /**
       * Archive_Tar Class constructor. This flavour of the constructor only
       * declare a new Archive_Tar object, identifying it by the name of the
-@@ -257,6 +264,18 @@ public function __construct($p_tarname, $p_compress = null)
+@@ -133,7 +143,7 @@ class Archive_Tar extends PEAR
+     public function __construct($p_tarname, $p_compress = null)
+     {
+         parent::__construct();
+-        
++
+         $this->_compress = false;
+         $this->_compress_type = 'none';
+         if (($p_compress === null) || ($p_compress == '')) {
+@@ -220,6 +230,19 @@ class Archive_Tar extends PEAR
                  return false;
              }
          }
 +
++
 +        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
 +            $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
 +                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
@@ -44,7 +57,7 @@ index 716511edee..90dd57386b 100644
      }
  
      public function __destruct()
-@@ -712,7 +731,7 @@ public function setAttribute()
+@@ -636,7 +659,7 @@ class Archive_Tar extends PEAR
          }
  
          // ----- Get the arguments
@@ -53,15 +66,16 @@ index 716511edee..90dd57386b 100644
  
          // ----- Read the attributes
          $i = 0;
-@@ -1392,10 +1411,20 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1314,10 +1337,22 @@ class Archive_Tar extends PEAR
          if ($p_stored_filename == '') {
              $p_stored_filename = $p_filename;
          }
 -        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
-+        $v_reduced_filename = $this->_pathReduction($p_stored_filename);
  
 -        if (strlen($v_reduce_filename) > 99) {
 -            if (!$this->_writeLongHeader($v_reduce_filename)) {
++        $v_reduced_filename = $this->_pathReduction($p_stored_filename);
++
 +        if (strlen($v_reduced_filename) > 99) {
 +            if (!$this->_writeLongHeader($v_reduced_filename, false)) {
 +                return false;
@@ -72,12 +86,13 @@ index 716511edee..90dd57386b 100644
 +        if (@is_link($p_filename)) {
 +            $v_linkname = readlink($p_filename);
 +        }
++
 +        if (strlen($v_linkname) > 99) {
 +            if (!$this->_writeLongHeader($v_linkname, true)) {
                  return false;
              }
          }
-@@ -1404,14 +1433,10 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1326,14 +1361,10 @@ class Archive_Tar extends PEAR
          $v_uid = sprintf("%07s", DecOct($v_info[4]));
          $v_gid = sprintf("%07s", DecOct($v_info[5]));
          $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
@@ -92,7 +107,7 @@ index 716511edee..90dd57386b 100644
              $v_size = sprintf("%011s", DecOct(0));
          } elseif (@is_dir($p_filename)) {
              $v_typeflag = "5";
-@@ -1423,7 +1448,6 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1345,7 +1376,6 @@ class Archive_Tar extends PEAR
          }
  
          $v_magic = 'ustar ';
@@ -100,13 +115,13 @@ index 716511edee..90dd57386b 100644
          $v_version = ' ';
  
          if (function_exists('posix_getpwuid')) {
-@@ -1438,14 +1462,13 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1360,14 +1390,12 @@ class Archive_Tar extends PEAR
          }
  
          $v_devmajor = '';
 -
          $v_devminor = '';
- 
+-
          $v_prefix = '';
  
          $v_binary_data_first = pack(
@@ -116,7 +131,7 @@ index 716511edee..90dd57386b 100644
              $v_perms,
              $v_uid,
              $v_gid,
-@@ -1485,7 +1508,7 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1407,7 +1435,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -125,7 +140,7 @@ index 716511edee..90dd57386b 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1517,7 +1540,7 @@ public function _writeHeaderBlock(
+@@ -1439,7 +1467,7 @@ class Archive_Tar extends PEAR
          $p_filename = $this->_pathReduction($p_filename);
  
          if (strlen($p_filename) > 99) {
@@ -134,7 +149,7 @@ index 716511edee..90dd57386b 100644
                  return false;
              }
          }
-@@ -1613,36 +1636,31 @@ public function _writeHeaderBlock(
+@@ -1535,36 +1563,31 @@ class Archive_Tar extends PEAR
       * @param string $p_filename
       * @return bool
       */
@@ -184,7 +199,7 @@ index 716511edee..90dd57386b 100644
          );
          $v_binary_data_last = pack(
              "a1a100a6a2a32a32a8a8a155a12",
-@@ -1677,7 +1695,7 @@ public function _writeLongHeader($p_filename)
+@@ -1599,7 +1622,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -193,7 +208,7 @@ index 716511edee..90dd57386b 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1718,28 +1736,12 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1640,28 +1663,13 @@ class Archive_Tar extends PEAR
          // ----- Calculate the checksum
          $v_checksum = 0;
          // ..... First part of the header
@@ -223,11 +238,12 @@ index 716511edee..90dd57386b 100644
 -                "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
 -        }
 -        $v_data = unpack($fmt, $v_binary_data);
++
 +        $v_data = unpack($this->_fmt, $v_binary_data);
  
          if (strlen($v_data["prefix"]) > 0) {
              $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
-@@ -1775,7 +1777,7 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1697,7 +1705,7 @@ class Archive_Tar extends PEAR
          $v_header['mode'] = OctDec(trim($v_data['mode']));
          $v_header['uid'] = OctDec(trim($v_data['uid']));
          $v_header['gid'] = OctDec(trim($v_data['gid']));
@@ -236,7 +252,7 @@ index 716511edee..90dd57386b 100644
          $v_header['mtime'] = OctDec(trim($v_data['mtime']));
          if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
              $v_header['size'] = 0;
-@@ -1794,6 +1796,41 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1716,6 +1724,40 @@ class Archive_Tar extends PEAR
          return true;
      }
  
@@ -273,28 +289,27 @@ index 716511edee..90dd57386b 100644
 +            return OctDec(trim($tar_size));
 +        }
 +    }
-+
 +
      /**
       * Detect and report a malicious file name
       *
-@@ -1803,10 +1840,13 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1725,10 +1767,13 @@ class Archive_Tar extends PEAR
       */
      private function _maliciousFilename($file)
      {
 -        if (strpos($file, '/../') !== false) {
 +        if (strpos($file, 'phar://') === 0) {
++            return true;
++        }
++        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
              return true;
          }
 -        if (strpos($file, '../') === 0) {
-+        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
-+            return true;
-+        }
 +        if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
              return true;
          }
          return false;
-@@ -1871,11 +1911,20 @@ private function _extractInString($p_filename)
+@@ -1793,11 +1838,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  
@@ -320,7 +335,7 @@ index 716511edee..90dd57386b 100644
              }
  
              if ($v_header['filename'] == $p_filename) {
-@@ -1976,11 +2025,20 @@ public function _extractList(
+@@ -1898,11 +1952,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  

Let's look at the diffs:

  1. The start at the start and the constructor are differences for PEAR and Drupal's slightly hacked version.
  2. The diffs around $v_reduced_filename = $this->_pathReduction($p_stored_filename);
    Here's core:
        public function _writeHeader($p_filename, $p_stored_filename)
        {
            if ($p_stored_filename == '') {
                $p_stored_filename = $p_filename;
            }
            $v_reduced_filename = $this->_pathReduction($p_stored_filename);
    
            if (strlen($v_reduced_filename) > 99) {
                if (!$this->_writeLongHeader($v_reduced_filename, false)) {
                    return false;
                }
            }
    
            $v_linkname = '';
            if (@is_link($p_filename)) {
                $v_linkname = readlink($p_filename);
            }
    

    Here's pear:

        public function _writeHeader($p_filename, $p_stored_filename)
        {
            if ($p_stored_filename == '') {
                $p_stored_filename = $p_filename;
            }
    
            $v_reduced_filename = $this->_pathReduction($p_stored_filename);
    
            if (strlen($v_reduced_filename) > 99) {
                if (!$this->_writeLongHeader($v_reduced_filename, false)) {
                    return false;
                }
            }
    
            $v_linkname = '';
            if (@is_link($p_filename)) {
                $v_linkname = readlink($p_filename);
            }
    

    So it looks as if pear has some added blank lines - let's add them.

  3. Diffs around $v_devminor = '';
    More blank lines - fixed.
  4. Diffs around $v_data = unpack($this->_fmt, $v_binary_data);
    More blank lines - fixed
  5. Diffs around return OctDec(trim($tar_size));
    Less blank lines - fixed
  6. _maliciousFilename
    Here's cores
        private function _maliciousFilename($file)
        {
            if (strpos($file, 'phar://') === 0) {
                return true;
            }
            if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
                return true;
            }
            if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
                return true;
            }
            return false;
        }
    

    and here is pear's

        private function _maliciousFilename($file)
        {
            if (strpos($file, 'phar://') === 0) {
                return true;
            }
            if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
                return true;
            }
            if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
                return true;
            }
            return false;
        }
    

    So all I can think is git diff got a bit confused here.

Following the same process on 8.x HEAD I see:

          $v_binary_data_first = pack(
              "a100a8a8a8a12a12",
 -            $v_reduce_filename,
++            $v_reduced_filename,
              $v_perms,
              $v_uid,
              $v_gid,

Which now sticks out like a sore thumb.

alexpott’s picture

Here's a patch based on making things the same. And a diff of the new diffs.

diff --git a/Users/alex/dev/drupal.diff b/Users/alex/dev/pear.diff
index b7e95082e1..93b05d82a6 100644
--- a/Users/alex/dev/drupal.diff
+++ b/Users/alex/dev/pear.diff
@@ -1,17 +1,21 @@
-diff --git a/core/lib/Drupal/Core/Archiver/ArchiveTar.php b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-index 716511edee..09c0c966d4 100644
---- a/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-+++ b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-@@ -42,7 +42,7 @@
+diff --git a/Archive/Tar.php b/Archive/Tar.php
+index 07550e6..68bdffe 100644
+--- a/Archive/Tar.php
++++ b/Archive/Tar.php
+@@ -39,7 +39,11 @@
+  * @link      http://pear.php.net/package/Archive_Tar
+  */
  
-  /**
-  * Note on Drupal 8 porting.
-- * This file origin is Tar.php, release 1.4.0 (stable) with some code
-+ * This file origin is Tar.php, release 1.4.5 (stable) with some code
-  * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
-  * To simplify future porting from pear of this file, you should not
-  * do cosmetic or other non significant changes to this file.
-@@ -151,6 +151,13 @@ class ArchiveTar
+-require_once 'PEAR.php';
++// If the PEAR class cannot be loaded via the autoloader,
++// then try to require_once it from the PHP include path.
++if (!class_exists('PEAR')) {
++    require_once 'PEAR.php';
++}
+ 
+ define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+ define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
+@@ -115,6 +119,12 @@ class Archive_Tar extends PEAR
       */
      public $error_object = null;
  
@@ -21,15 +25,24 @@ index 716511edee..09c0c966d4 100644
 +     * @var string
 +     */
 +    public $_fmt ='';
-+
      /**
       * Archive_Tar Class constructor. This flavour of the constructor only
       * declare a new Archive_Tar object, identifying it by the name of the
-@@ -257,6 +264,18 @@ public function __construct($p_tarname, $p_compress = null)
+@@ -133,7 +143,7 @@ class Archive_Tar extends PEAR
+     public function __construct($p_tarname, $p_compress = null)
+     {
+         parent::__construct();
+-        
++
+         $this->_compress = false;
+         $this->_compress_type = 'none';
+         if (($p_compress === null) || ($p_compress == '')) {
+@@ -220,6 +230,19 @@ class Archive_Tar extends PEAR
                  return false;
              }
          }
 +
++
 +        if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
 +            $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
 +                   "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
@@ -44,7 +57,7 @@ index 716511edee..09c0c966d4 100644
      }
  
      public function __destruct()
-@@ -712,7 +731,7 @@ public function setAttribute()
+@@ -636,7 +659,7 @@ class Archive_Tar extends PEAR
          }
  
          // ----- Get the arguments
@@ -53,7 +66,7 @@ index 716511edee..09c0c966d4 100644
  
          // ----- Read the attributes
          $i = 0;
-@@ -1392,10 +1411,22 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1314,10 +1337,22 @@ class Archive_Tar extends PEAR
          if ($p_stored_filename == '') {
              $p_stored_filename = $p_filename;
          }
@@ -79,7 +92,7 @@ index 716511edee..09c0c966d4 100644
                  return false;
              }
          }
-@@ -1404,14 +1435,10 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1326,14 +1361,10 @@ class Archive_Tar extends PEAR
          $v_uid = sprintf("%07s", DecOct($v_info[4]));
          $v_gid = sprintf("%07s", DecOct($v_info[5]));
          $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
@@ -94,7 +107,7 @@ index 716511edee..09c0c966d4 100644
              $v_size = sprintf("%011s", DecOct(0));
          } elseif (@is_dir($p_filename)) {
              $v_typeflag = "5";
-@@ -1423,7 +1450,6 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1345,7 +1376,6 @@ class Archive_Tar extends PEAR
          }
  
          $v_magic = 'ustar ';
@@ -102,7 +115,7 @@ index 716511edee..09c0c966d4 100644
          $v_version = ' ';
  
          if (function_exists('posix_getpwuid')) {
-@@ -1438,14 +1464,12 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1360,14 +1390,12 @@ class Archive_Tar extends PEAR
          }
  
          $v_devmajor = '';
@@ -118,7 +131,7 @@ index 716511edee..09c0c966d4 100644
              $v_perms,
              $v_uid,
              $v_gid,
-@@ -1485,7 +1509,7 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1407,7 +1435,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -127,7 +140,7 @@ index 716511edee..09c0c966d4 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1517,7 +1541,7 @@ public function _writeHeaderBlock(
+@@ -1439,7 +1467,7 @@ class Archive_Tar extends PEAR
          $p_filename = $this->_pathReduction($p_filename);
  
          if (strlen($p_filename) > 99) {
@@ -136,7 +149,7 @@ index 716511edee..09c0c966d4 100644
                  return false;
              }
          }
-@@ -1613,36 +1637,31 @@ public function _writeHeaderBlock(
+@@ -1535,36 +1563,31 @@ class Archive_Tar extends PEAR
       * @param string $p_filename
       * @return bool
       */
@@ -186,7 +199,7 @@ index 716511edee..09c0c966d4 100644
          );
          $v_binary_data_last = pack(
              "a1a100a6a2a32a32a8a8a155a12",
-@@ -1677,7 +1696,7 @@ public function _writeLongHeader($p_filename)
+@@ -1599,7 +1622,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -195,7 +208,7 @@ index 716511edee..09c0c966d4 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1718,28 +1737,13 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1640,28 +1663,13 @@ class Archive_Tar extends PEAR
          // ----- Calculate the checksum
          $v_checksum = 0;
          // ..... First part of the header
@@ -230,7 +243,7 @@ index 716511edee..09c0c966d4 100644
  
          if (strlen($v_data["prefix"]) > 0) {
              $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
-@@ -1775,7 +1779,7 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1697,7 +1705,7 @@ class Archive_Tar extends PEAR
          $v_header['mode'] = OctDec(trim($v_data['mode']));
          $v_header['uid'] = OctDec(trim($v_data['uid']));
          $v_header['gid'] = OctDec(trim($v_data['gid']));
@@ -239,7 +252,7 @@ index 716511edee..09c0c966d4 100644
          $v_header['mtime'] = OctDec(trim($v_data['mtime']));
          if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
              $v_header['size'] = 0;
-@@ -1794,6 +1798,40 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1716,6 +1724,40 @@ class Archive_Tar extends PEAR
          return true;
      }
  
@@ -280,23 +293,23 @@ index 716511edee..09c0c966d4 100644
      /**
       * Detect and report a malicious file name
       *
-@@ -1803,10 +1841,13 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1725,10 +1767,13 @@ class Archive_Tar extends PEAR
       */
      private function _maliciousFilename($file)
      {
 -        if (strpos($file, '/../') !== false) {
 +        if (strpos($file, 'phar://') === 0) {
++            return true;
++        }
++        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
              return true;
          }
 -        if (strpos($file, '../') === 0) {
-+        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
-+            return true;
-+        }
 +        if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
              return true;
          }
          return false;
-@@ -1871,11 +1912,20 @@ private function _extractInString($p_filename)
+@@ -1793,11 +1838,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  
@@ -322,7 +335,7 @@ index 716511edee..09c0c966d4 100644
              }
  
              if ($v_header['filename'] == $p_filename) {
-@@ -1976,11 +2026,20 @@ public function _extractList(
+@@ -1898,11 +1952,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  

As before there's some oddness in _maliciousFilename() but I think that is git diff and so many similar lines.

alexpott’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
822 bytes
2.27 KB

Missed a couple more new lines - adding them in to make the diff of the diffs are small as possible.

diff --git a/Users/alex/dev/drupal.diff b/Users/alex/dev/pear.diff
index a0b5087876..93b05d82a6 100644
--- a/Users/alex/dev/drupal.diff
+++ b/Users/alex/dev/pear.diff
@@ -1,17 +1,21 @@
-diff --git a/core/lib/Drupal/Core/Archiver/ArchiveTar.php b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-index 716511edee..12722e69df 100644
---- a/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-+++ b/core/lib/Drupal/Core/Archiver/ArchiveTar.php
-@@ -42,7 +42,7 @@
+diff --git a/Archive/Tar.php b/Archive/Tar.php
+index 07550e6..68bdffe 100644
+--- a/Archive/Tar.php
++++ b/Archive/Tar.php
+@@ -39,7 +39,11 @@
+  * @link      http://pear.php.net/package/Archive_Tar
+  */
  
-  /**
-  * Note on Drupal 8 porting.
-- * This file origin is Tar.php, release 1.4.0 (stable) with some code
-+ * This file origin is Tar.php, release 1.4.5 (stable) with some code
-  * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
-  * To simplify future porting from pear of this file, you should not
-  * do cosmetic or other non significant changes to this file.
-@@ -151,6 +151,12 @@ class ArchiveTar
+-require_once 'PEAR.php';
++// If the PEAR class cannot be loaded via the autoloader,
++// then try to require_once it from the PHP include path.
++if (!class_exists('PEAR')) {
++    require_once 'PEAR.php';
++}
+ 
+ define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+ define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
+@@ -115,6 +119,12 @@ class Archive_Tar extends PEAR
       */
      public $error_object = null;
  
@@ -24,7 +28,16 @@ index 716511edee..12722e69df 100644
      /**
       * Archive_Tar Class constructor. This flavour of the constructor only
       * declare a new Archive_Tar object, identifying it by the name of the
-@@ -257,6 +263,19 @@ public function __construct($p_tarname, $p_compress = null)
+@@ -133,7 +143,7 @@ class Archive_Tar extends PEAR
+     public function __construct($p_tarname, $p_compress = null)
+     {
+         parent::__construct();
+-        
++
+         $this->_compress = false;
+         $this->_compress_type = 'none';
+         if (($p_compress === null) || ($p_compress == '')) {
+@@ -220,6 +230,19 @@ class Archive_Tar extends PEAR
                  return false;
              }
          }
@@ -44,7 +57,7 @@ index 716511edee..12722e69df 100644
      }
  
      public function __destruct()
-@@ -712,7 +731,7 @@ public function setAttribute()
+@@ -636,7 +659,7 @@ class Archive_Tar extends PEAR
          }
  
          // ----- Get the arguments
@@ -53,7 +66,7 @@ index 716511edee..12722e69df 100644
  
          // ----- Read the attributes
          $i = 0;
-@@ -1392,10 +1411,22 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1314,10 +1337,22 @@ class Archive_Tar extends PEAR
          if ($p_stored_filename == '') {
              $p_stored_filename = $p_filename;
          }
@@ -79,7 +92,7 @@ index 716511edee..12722e69df 100644
                  return false;
              }
          }
-@@ -1404,14 +1435,10 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1326,14 +1361,10 @@ class Archive_Tar extends PEAR
          $v_uid = sprintf("%07s", DecOct($v_info[4]));
          $v_gid = sprintf("%07s", DecOct($v_info[5]));
          $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
@@ -94,7 +107,7 @@ index 716511edee..12722e69df 100644
              $v_size = sprintf("%011s", DecOct(0));
          } elseif (@is_dir($p_filename)) {
              $v_typeflag = "5";
-@@ -1423,7 +1450,6 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1345,7 +1376,6 @@ class Archive_Tar extends PEAR
          }
  
          $v_magic = 'ustar ';
@@ -102,7 +115,7 @@ index 716511edee..12722e69df 100644
          $v_version = ' ';
  
          if (function_exists('posix_getpwuid')) {
-@@ -1438,14 +1464,12 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1360,14 +1390,12 @@ class Archive_Tar extends PEAR
          }
  
          $v_devmajor = '';
@@ -118,7 +131,7 @@ index 716511edee..12722e69df 100644
              $v_perms,
              $v_uid,
              $v_gid,
-@@ -1485,7 +1509,7 @@ public function _writeHeader($p_filename, $p_stored_filename)
+@@ -1407,7 +1435,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -127,7 +140,7 @@ index 716511edee..12722e69df 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1517,7 +1541,7 @@ public function _writeHeaderBlock(
+@@ -1439,7 +1467,7 @@ class Archive_Tar extends PEAR
          $p_filename = $this->_pathReduction($p_filename);
  
          if (strlen($p_filename) > 99) {
@@ -136,7 +149,7 @@ index 716511edee..12722e69df 100644
                  return false;
              }
          }
-@@ -1613,36 +1637,31 @@ public function _writeHeaderBlock(
+@@ -1535,36 +1563,31 @@ class Archive_Tar extends PEAR
       * @param string $p_filename
       * @return bool
       */
@@ -186,7 +199,7 @@ index 716511edee..12722e69df 100644
          );
          $v_binary_data_last = pack(
              "a1a100a6a2a32a32a8a8a155a12",
-@@ -1677,7 +1696,7 @@ public function _writeLongHeader($p_filename)
+@@ -1599,7 +1622,7 @@ class Archive_Tar extends PEAR
          $this->_writeBlock($v_binary_data_first, 148);
  
          // ----- Write the calculated checksum
@@ -195,7 +208,7 @@ index 716511edee..12722e69df 100644
          $v_binary_data = pack("a8", $v_checksum);
          $this->_writeBlock($v_binary_data, 8);
  
-@@ -1718,28 +1737,13 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1640,28 +1663,13 @@ class Archive_Tar extends PEAR
          // ----- Calculate the checksum
          $v_checksum = 0;
          // ..... First part of the header
@@ -230,7 +243,7 @@ index 716511edee..12722e69df 100644
  
          if (strlen($v_data["prefix"]) > 0) {
              $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
-@@ -1775,7 +1779,7 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1697,7 +1705,7 @@ class Archive_Tar extends PEAR
          $v_header['mode'] = OctDec(trim($v_data['mode']));
          $v_header['uid'] = OctDec(trim($v_data['uid']));
          $v_header['gid'] = OctDec(trim($v_data['gid']));
@@ -239,7 +252,7 @@ index 716511edee..12722e69df 100644
          $v_header['mtime'] = OctDec(trim($v_data['mtime']));
          if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
              $v_header['size'] = 0;
-@@ -1794,6 +1798,40 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1716,6 +1724,40 @@ class Archive_Tar extends PEAR
          return true;
      }
  
@@ -280,23 +293,23 @@ index 716511edee..12722e69df 100644
      /**
       * Detect and report a malicious file name
       *
-@@ -1803,10 +1841,13 @@ public function _readHeader($v_binary_data, &$v_header)
+@@ -1725,10 +1767,13 @@ class Archive_Tar extends PEAR
       */
      private function _maliciousFilename($file)
      {
 -        if (strpos($file, '/../') !== false) {
 +        if (strpos($file, 'phar://') === 0) {
++            return true;
++        }
++        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
              return true;
          }
 -        if (strpos($file, '../') === 0) {
-+        if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
-+            return true;
-+        }
 +        if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
              return true;
          }
          return false;
-@@ -1871,11 +1912,20 @@ private function _extractInString($p_filename)
+@@ -1793,11 +1838,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  
@@ -322,7 +335,7 @@ index 716511edee..12722e69df 100644
              }
  
              if ($v_header['filename'] == $p_filename) {
-@@ -1976,11 +2026,20 @@ public function _extractList(
+@@ -1898,11 +1952,20 @@ class Archive_Tar extends PEAR
                  continue;
              }
  

For me this is now rtbc - we've proved that all the differences are between the Drupal changes and pear changes are due to Drupal now using the full Pear library and just this one class - that all happens in the constructor. The rest of the file - the changes are the same.

I have only added whitespace to the patch.

Ayesh’s picture

I worked on the primary patch for the Archive_Tar update for the security release. It was a manual backport because Drupal core had some changes and even with minor hunk changes, an upstream patch didn't apply well. I apologize about the mistakes in it :(

I will double check the Drupal 7's port as well, but it was a direct patch from upstream so I'm believe this doesn't apply to 7.x.

xjm’s picture

Congrats on your first experience breaking core HEAD, Ayesh. It's a rite of passage. :D

Leaving RTBC while tests finish running. If it turns out to need a fix in 7.x as well, we'll move the issue to "Patch (to be ported)" once we commit it to the 8.x branches.

  • xjm committed b0a6389 on 8.7.x
    Issue #3026470 by alexpott, jrockowitz, Joseph Zhao: ArchiveTar is...

  • xjm committed 5525e51 on 8.6.x
    Issue #3026470 by alexpott, jrockowitz, Joseph Zhao: ArchiveTar is...

  • xjm committed 4135c14 on 8.5.x
    Issue #3026470 by alexpott, jrockowitz, Joseph Zhao: ArchiveTar is...
xjm’s picture

Status: Reviewed & tested by the community » Fixed

Alright, committed to 8.7.x, 8.6.x, and 8.5.x. This will be available in the next bugfix release on Feb. 6 if not sooner. In the meanwhile, sites affected can apply the above patch or use the next development tarball.

If it turns out to affect 7.x too we can reopen the issue with "Patch (to be ported)".

Thanks!

jrockowitz’s picture

@everyone Thanks for fixing and committing this.

Waiting to Feb 6 should be fine. This issue is documented in the Webform issue queue and people can apply the patch.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

xjm’s picture

Version: 8.7.x-dev » 8.5.x-dev