Fix CVE-2018-20482

* 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.
This commit is contained in:
Sergey Poznyakoff
2018-12-27 17:48:57 +02:00
parent 3c2a2cd94d
commit c15c42ccd1
7 changed files with 231 additions and 9 deletions

8
NEWS
View File

@@ -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>
@@ -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.
version 1.30 - Sergey Poznyakoff, 2017-12-17

View 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;

View 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\

62
tests/sptrcreat.at Normal file
View File

@@ -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

55
tests/sptrdiff00.at Normal file
View File

@@ -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

55
tests/sptrdiff01.at Normal file
View File

@@ -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

View 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])