]> git.ipfire.org Git - thirdparty/tar.git/commitdiff
Fix CVE-2018-20482
authorSergey Poznyakoff <gray@gnu.org>
Thu, 27 Dec 2018 15:48:57 +0000 (17:48 +0200)
committerSergey Poznyakoff <gray@gnu.org>
Thu, 27 Dec 2018 15:53:38 +0000 (17:53 +0200)
* NEWS: Update.
* src/sparse.c (sparse_dump_region): Handle short read condition.
(sparse_extract_region,check_data_region): Fix dumped_size calculation.
Handle short read condition.
(pax_decode_header): Fix dumped_size calculation.
* tests/Makefile.am: Add new testcases.
* tests/testsuite.at: Likewise.

* tests/sptrcreat.at: New file.
* tests/sptrdiff00.at: New file.
* tests/sptrdiff01.at: New file.

NEWS
src/sparse.c
tests/Makefile.am
tests/sptrcreat.at [new file with mode: 0644]
tests/sptrdiff00.at [new file with mode: 0644]
tests/sptrdiff01.at [new file with mode: 0644]
tests/testsuite.at

diff --git a/NEWS b/NEWS
index 93f3197588a3a41fee6fee45bbd80113079e6311..9debbeb0b63fca2d2bc9a3eb3dd3c7d1559c22f0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU tar NEWS - User visible changes. 2018-12-21
+GNU tar NEWS - User visible changes. 2018-12-27
 Please send GNU tar bug reports to <bug-tar@gnu.org>
 
 \f
@@ -25,6 +25,12 @@ semantics of the option.
 Previous versions of tar extracted NAME, those of named members that
 appeared before it, and everything after it.
 
+* Fix CVE-2018-20482
+
+When creating archives with the --sparse option, previous versions of
+tar would loop endlessly if a sparse file had been truncated while
+being archived.
+
 \f
 version 1.30 - Sergey Poznyakoff, 2017-12-17
 
index d41c0eacd1f355284a4cdb185c1d3c00bf4f0c08..f611200a2fc5c4feb4a06a9fa40aae2d0489c764 100644 (file)
@@ -1,6 +1,6 @@
 /* Functions for dealing with sparse files
 
-   Copyright 2003-2007, 2010, 2013-2017 Free Software Foundation, Inc.
+   Copyright 2003-2007, 2010, 2013-2018 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
@@ -427,6 +427,30 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
                             bufsize);
          return false;
        }
+      else if (bytes_read == 0)
+       {
+         char buf[UINTMAX_STRSIZE_BOUND];
+         struct stat st;
+         size_t n;
+         if (fstat (file->fd, &st) == 0)
+           n = file->stat_info->stat.st_size - st.st_size;
+         else
+           n = file->stat_info->stat.st_size
+             - (file->stat_info->sparse_map[i].offset
+                + file->stat_info->sparse_map[i].numbytes
+                - bytes_left);
+         
+         WARNOPT (WARN_FILE_SHRANK,
+                  (0, 0,
+                   ngettext ("%s: File shrank by %s byte; padding with zeros",
+                             "%s: File shrank by %s bytes; padding with zeros",
+                             n),
+                   quotearg_colon (file->stat_info->orig_file_name),
+                   STRINGIFY_BIGINT (n, buf)));
+         if (! ignore_failed_read_option)
+           set_exit_status (TAREXIT_DIFFERS);
+         return false;
+       }
 
       memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
       bytes_left -= bytes_read;
@@ -464,9 +488,9 @@ sparse_extract_region (struct tar_sparse_file *file, size_t i)
          return false;
        }
       set_next_block_after (blk);
+      file->dumped_size += BLOCKSIZE;
       count = blocking_write (file->fd, blk->buffer, wrbytes);
       write_size -= count;
-      file->dumped_size += count;
       mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       file->offset += count;
       if (count != wrbytes)
@@ -598,6 +622,12 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
                             rdsize);
          return false;
        }
+      else if (bytes_read == 0)
+       {
+         report_difference (file->stat_info, _("Size differs"));
+         return false;
+       }
+      
       if (!zero_block_p (diff_buffer, bytes_read))
        {
          char begbuf[INT_BUFSIZE_BOUND (off_t)];
@@ -609,6 +639,7 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 
       beg += bytes_read;
     }
+
   return true;
 }
 
@@ -635,6 +666,7 @@ check_data_region (struct tar_sparse_file *file, size_t i)
          return false;
        }
       set_next_block_after (blk);
+      file->dumped_size += BLOCKSIZE;      
       bytes_read = safe_read (file->fd, diff_buffer, rdsize);
       if (bytes_read == SAFE_READ_ERROR)
        {
@@ -645,7 +677,11 @@ check_data_region (struct tar_sparse_file *file, size_t i)
                             rdsize);
          return false;
        }
-      file->dumped_size += bytes_read;
+      else if (bytes_read == 0)
+       {
+         report_difference (&current_stat_info, _("Size differs"));
+         return false;
+       }
       size_left -= bytes_read;
       mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
       if (memcmp (blk->buffer, diff_buffer, rdsize))
@@ -1213,7 +1249,8 @@ pax_decode_header (struct tar_sparse_file *file)
       union block *blk;
       char *p;
       size_t i;
-
+      off_t start;
+      
 #define COPY_BUF(b,buf,src) do                                     \
  {                                                                 \
    char *endp = b->buffer + BLOCKSIZE;                             \
@@ -1229,7 +1266,6 @@ pax_decode_header (struct tar_sparse_file *file)
        if (src == endp)                                            \
         {                                                         \
           set_next_block_after (b);                               \
-           file->dumped_size += BLOCKSIZE;                         \
            b = find_next_block ();                                 \
            src = b->buffer;                                        \
           endp = b->buffer + BLOCKSIZE;                           \
@@ -1240,8 +1276,8 @@ pax_decode_header (struct tar_sparse_file *file)
    dst[-1] = 0;                                                    \
  } while (0)
 
+      start = current_block_ordinal ();
       set_next_block_after (current_header);
-      file->dumped_size += BLOCKSIZE;
       blk = find_next_block ();
       p = blk->buffer;
       COPY_BUF (blk,nbuf,p);
@@ -1278,6 +1314,8 @@ pax_decode_header (struct tar_sparse_file *file)
          sparse_add_map (file->stat_info, &sp);
        }
       set_next_block_after (blk);
+
+      file->dumped_size += BLOCKSIZE * (current_block_ordinal () - start);
     }
 
   return true;
index fb463252f5544a3b344d84aa86dc772abdb3faa6..7f9294f0c446d36426dccd23ac11f11177217e58 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile for GNU tar regression tests.
 
-# Copyright 1996-1997, 1999-2001, 2003-2007, 2009, 2012-2015 Free Software
+# Copyright 1996-1997, 1999-2001, 2003-2007, 2009, 2012-2018 Free Software
 
 # This file is part of GNU tar.
 
@@ -238,6 +238,9 @@ TESTSUITE_AT = \
  spmvp00.at\
  spmvp01.at\
  spmvp10.at\
+ sptrcreat.at\
+ sptrdiff00.at\
+ sptrdiff01.at\
  time01.at\
  time02.at\
  truncate.at\
diff --git a/tests/sptrcreat.at b/tests/sptrcreat.at
new file mode 100644 (file)
index 0000000..8e28f0e
--- /dev/null
@@ -0,0 +1,62 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright 2018 Free Software Foundation, Inc.
+
+# This file is part of GNU tar.
+
+# GNU tar is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU tar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Tar up to 1.30 would loop endlessly if a sparse file had been truncated
+# while being archived (with --sparse flag).
+#
+# The bug has been assigned id CVE-2018-20482 (on the grounds that it is a
+# denial of service possibility).
+# 
+# Reported by: Chris Siebenmann <cks.gnutar-01@cs.toronto.edu>
+# References: <20181226223948.781EB32008E@apps1.cs.toronto.edu>,
+#   <http://lists.gnu.org/archive/html/bug-tar/2018-12/msg00023.html>
+#   <https://utcc.utoronto.ca/~cks/space/blog/sysadmin/TarFindingTruncateBug>
+#   <https://nvd.nist.gov/vuln/detail/CVE-2018-20482>
+
+AT_SETUP([sparse file truncated while archiving])
+AT_KEYWORDS([truncate filechange sparse sptr sptrcreat])
+
+AT_TAR_CHECK([
+genfile --sparse --block-size=1024 --file foo \
+  0 ABCDEFGHIJ 1M ABCDEFGHIJ 10M ABCDEFGHIJ 200M ABCDEFGHIJ
+genfile --file baz
+genfile --run --checkpoint 3 --length 200m --truncate foo -- \
+ tar --checkpoint=1 \
+     --checkpoint-action=echo \
+     --checkpoint-action=sleep=1 \
+     --sparse -vcf bar foo baz
+echo Exit status: $?
+echo separator
+genfile --file foo --seek 200m --length 11575296 --pattern=zeros
+tar dvf bar],
+[1],
+[foo
+baz
+Exit status: 1
+separator
+foo
+foo: Mod time differs
+baz
+],
+[tar: foo: File shrank by 11575296 bytes; padding with zeros
+],
+[],[],[posix, gnu, oldgnu])
+
+AT_CLEANUP
diff --git a/tests/sptrdiff00.at b/tests/sptrdiff00.at
new file mode 100644 (file)
index 0000000..c410561
--- /dev/null
@@ -0,0 +1,55 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This file is part of GNU tar.
+#
+# GNU tar is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GNU tar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# While fixing CVE-2018-20482 (see sptrcreat.at) it has been discovered
+# that similar bug exists in file checking code (tar d). 
+# This test case checks if tar correctly handles a short read condition
+# appearing in check_sparse_region.
+
+AT_SETUP([file truncated in sparse region while comparing])
+AT_KEYWORDS([truncate filechange sparse sptr sptrdiff diff])
+
+# This triggers short read in check_sparse_region.
+AT_TAR_CHECK([
+genfile --sparse --block-size=1024 --file foo \
+  0 ABCDEFGHIJ 1M ABCDEFGHIJ 10M ABCDEFGHIJ 200M ABCDEFGHIJ
+genfile --file baz
+echo creating
+tar --sparse -vcf bar foo baz
+echo comparing
+genfile --run --checkpoint 3 --length 200m --truncate foo -- \
+ tar --checkpoint=1 \
+     --checkpoint-action=echo='Write checkpoint %u' \
+     --checkpoint-action=sleep=1 \
+     --sparse -vdf bar 
+],
+[1],
+[creating
+foo
+baz
+comparing
+foo
+foo: Size differs
+baz
+],
+[],
+[],[],[posix, gnu, oldgnu])
+
+AT_CLEANUP
diff --git a/tests/sptrdiff01.at b/tests/sptrdiff01.at
new file mode 100644 (file)
index 0000000..2da2267
--- /dev/null
@@ -0,0 +1,55 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+#
+# Test suite for GNU tar.
+# Copyright 2018 Free Software Foundation, Inc.
+#
+# This file is part of GNU tar.
+#
+# GNU tar is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GNU tar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# While fixing CVE-2018-20482 (see sptrcreat.at) it has been discovered
+# that similar bug exists in file checking code (tar d). 
+# This test case checks if tar correctly handles a short read condition
+# appearing in check_data_region.
+
+AT_SETUP([file truncated in data region while comparing])
+AT_KEYWORDS([truncate filechange sparse sptr sptrdiff diff])
+
+# This triggers short read in check_data_region.
+AT_TAR_CHECK([
+genfile --sparse --block-size=1024 --file foo \
+  0 ABCDEFGHIJ 1M ABCDEFGHIJ 10M ABCDEFGHIJ 200M ABCDEFGHIJ
+genfile --file baz
+echo creating
+tar --sparse -vcf bar foo baz
+echo comparing
+genfile --run --checkpoint 5 --length 221278210 --truncate foo -- \
+ tar --checkpoint=1 \
+     --checkpoint-action=echo='Write checkpoint %u' \
+     --checkpoint-action=sleep=1 \
+     --sparse -vdf bar 
+],
+[1],
+[creating
+foo
+baz
+comparing
+foo
+foo: Size differs
+baz
+],
+[],
+[],[],[posix, gnu, oldgnu])
+
+AT_CLEANUP
index dcccc7b1733797bfe6b2f2d64c54956abb94b3c3..0f5cca3c865f3e73a43205f2c4c3481acc80c86a 100644 (file)
@@ -1,7 +1,7 @@
 # Process this file with autom4te to create testsuite. -*- Autotest -*-
 
 # Test suite for GNU tar.
-# Copyright 2004-2008, 2010-2017 Free Software Foundation, Inc.
+# Copyright 2004-2008, 2010-2018 Free Software Foundation, Inc.
 
 # This file is part of GNU tar.
 
@@ -416,6 +416,9 @@ m4_include([sparsemv.at])
 m4_include([spmvp00.at])
 m4_include([spmvp01.at])
 m4_include([spmvp10.at])
+m4_include([sptrcreat.at])
+m4_include([sptrdiff00.at])
+m4_include([sptrdiff01.at])
 
 AT_BANNER([Updates])
 m4_include([update.at])