tar/Fix-CVE-2018-20482.patch
2019-12-25 16:08:38 +08:00

466 lines
15 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From fd555fc06bfb4eae3eb48fbc150d20a4dda0d150 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org>
Date: Thu, 27 Dec 2018 17:48:57 +0200
Subject: [PATCH] Fixed a denial of service when the '--sparse' option
mishandles file shrinkage during read access(CVE-2018-20482)
https://github.com/praiskup/tar/commit/c15c42ccd1e2377945fd0414eca1a49294bff454
Signed-off-by: fangyufa1 <fangyufa1@huawei.com>
---
NEWS | 32 +++++++++++++++++-
src/sparse.c | 82 +++++++++++++++++++++++++++++----------------
tests/Makefile.am | 5 ++-
tests/sptrcreat.at | 62 ++++++++++++++++++++++++++++++++++
tests/sptrdiff00.at | 55 ++++++++++++++++++++++++++++++
tests/sptrdiff01.at | 55 ++++++++++++++++++++++++++++++
tests/testsuite.at | 5 ++-
7 files changed, 264 insertions(+), 32 deletions(-)
create mode 100644 tests/sptrcreat.at
create mode 100644 tests/sptrdiff00.at
create mode 100644 tests/sptrdiff01.at
diff --git a/NEWS b/NEWS
index cd15fa1..283c376 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,37 @@
-GNU tar NEWS - User visible changes. 2017-12-17
+GNU tar NEWS - User visible changes. 2018-12-27
Please send GNU tar bug reports to <bug-tar@gnu.org>
+version 1.30.90 (Git)
+
+* Fix heap-buffer-overrun with --one-top-level.
+Bug introduced with the addition of that option in 1.28.
+
+* Support for zstd compression
+
+New option '--zstd' instructs tar to use zstd as compression program.
+When listing, extractng and comparing, zstd compressed archives are
+recognized automatically.
+When '-a' option is in effect, zstd compression is selected if the
+destination archive name ends in '.zst' or '.tzst'.
+
+* The -K option interacts properly with member names given in the command line
+
+Names of members to extract can be specified along with the "-K NAME"
+option. In this case, tar will extract NAME and those of named members
+that appear in the archive after it, which is consistent with the
+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.
+
+
version 1.30 - Sergey Poznyakoff, 2017-12-17
* Member names containing '..' components are now skipped when extracting.
diff --git a/src/sparse.c b/src/sparse.c
index 6ec069d..726facd 100644
--- a/src/sparse.c
+++ b/src/sparse.c
@@ -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
@@ -428,16 +428,30 @@ sparse_dump_region (struct tar_sparse_file *file, size_t i)
bufsize);
return false;
}
- if (bytes_read == 0)
- {
- char buf[UINTMAX_STRSIZE_BOUND];
- FATAL_ERROR ((0, 0,
- ngettext ("%s: File shrank by %s byte",
- "%s: File shrank by %s bytes",
- bytes_left),
- quotearg_colon (file_name),
- offtostr (bytes_left, buf)));
- }
+ 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_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;
@@ -475,9 +489,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)
@@ -610,20 +624,24 @@ check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
offset, rdsize);
return false;
}
-
- if (bytes_read == 0
- || !zero_block_p (diff_buffer, bytes_read))
- {
- char begbuf[INT_BUFSIZE_BOUND (off_t)];
- const char *msg = bytes_read ? _("File fragment at %s is not a hole")
- : _("Hole starting at %s is truncated");
-
- report_difference (file->stat_info, msg, offtostr (beg, begbuf));
- 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)];
+ report_difference (file->stat_info,
+ _("File fragment at %s is not a hole"),
+ offtostr (offset, begbuf));
+ return false;
+ }
offset += bytes_read;
}
+
return true;
}
@@ -650,6 +668,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)
{
@@ -660,11 +679,14 @@ 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 (bytes_read == 0
- || memcmp (blk->buffer, diff_buffer, bytes_read))
+ if (memcmp (blk->buffer, diff_buffer, bytes_read))
{
report_difference (file->stat_info, _("Contents differ"));
return false;
@@ -1229,7 +1251,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; \
@@ -1245,7 +1268,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 (); \
if (!b) \
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive"))); \
@@ -1258,8 +1280,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 ();
if (!blk)
FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
@@ -1298,6 +1320,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;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2d7939d..ac3b6e7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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.
@@ -228,6 +228,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
index 0000000..8e28f0e
--- /dev/null
+++ b/tests/sptrcreat.at
@@ -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
index 0000000..c410561
--- /dev/null
+++ b/tests/sptrdiff00.at
@@ -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
index 0000000..2da2267
--- /dev/null
+++ b/tests/sptrdiff01.at
@@ -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
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 2a83757..23386f7 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -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.
@@ -405,6 +405,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])
--
2.19.1