Merge with 3.5: Issue #24259: tarfile now raises a ReadError if an archive is truncated inside a data segment.

This commit is contained in:
Lars Gustäbel 2015-07-06 09:32:05 +02:00
commit 7afe40e40e
3 changed files with 41 additions and 7 deletions

View File

@ -225,7 +225,7 @@ def calc_chksums(buf):
signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf)) signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf))
return unsigned_chksum, signed_chksum return unsigned_chksum, signed_chksum
def copyfileobj(src, dst, length=None): def copyfileobj(src, dst, length=None, exception=OSError):
"""Copy length bytes from fileobj src to fileobj dst. """Copy length bytes from fileobj src to fileobj dst.
If length is None, copy the entire content. If length is None, copy the entire content.
""" """
@ -240,13 +240,13 @@ def copyfileobj(src, dst, length=None):
for b in range(blocks): for b in range(blocks):
buf = src.read(BUFSIZE) buf = src.read(BUFSIZE)
if len(buf) < BUFSIZE: if len(buf) < BUFSIZE:
raise OSError("end of file reached") raise exception("unexpected end of data")
dst.write(buf) dst.write(buf)
if remainder != 0: if remainder != 0:
buf = src.read(remainder) buf = src.read(remainder)
if len(buf) < remainder: if len(buf) < remainder:
raise OSError("end of file reached") raise exception("unexpected end of data")
dst.write(buf) dst.write(buf)
return return
@ -690,7 +690,10 @@ class _FileInFile(object):
length = min(size, stop - self.position) length = min(size, stop - self.position)
if data: if data:
self.fileobj.seek(offset + (self.position - start)) self.fileobj.seek(offset + (self.position - start))
buf += self.fileobj.read(length) b = self.fileobj.read(length)
if len(b) != length:
raise ReadError("unexpected end of data")
buf += b
else: else:
buf += NUL * length buf += NUL * length
size -= length size -= length
@ -2150,9 +2153,9 @@ class TarFile(object):
if tarinfo.sparse is not None: if tarinfo.sparse is not None:
for offset, size in tarinfo.sparse: for offset, size in tarinfo.sparse:
target.seek(offset) target.seek(offset)
copyfileobj(source, target, size) copyfileobj(source, target, size, ReadError)
else: else:
copyfileobj(source, target, tarinfo.size) copyfileobj(source, target, tarinfo.size, ReadError)
target.seek(tarinfo.size) target.seek(tarinfo.size)
target.truncate() target.truncate()
@ -2267,8 +2270,13 @@ class TarFile(object):
self.firstmember = None self.firstmember = None
return m return m
# Advance the file pointer.
if self.offset != self.fileobj.tell():
self.fileobj.seek(self.offset - 1)
if not self.fileobj.read(1):
raise ReadError("unexpected end of data")
# Read the next block. # Read the next block.
self.fileobj.seek(self.offset)
tarinfo = None tarinfo = None
while True: while True:
try: try:

View File

@ -364,6 +364,29 @@ class CommonReadTest(ReadTest):
finally: finally:
tar.close() tar.close()
def test_premature_end_of_archive(self):
for size in (512, 600, 1024, 1200):
with tarfile.open(tmpname, "w:") as tar:
t = tarfile.TarInfo("foo")
t.size = 1024
tar.addfile(t, io.BytesIO(b"a" * 1024))
with open(tmpname, "r+b") as fobj:
fobj.truncate(size)
with tarfile.open(tmpname) as tar:
with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
for t in tar:
pass
with tarfile.open(tmpname) as tar:
t = tar.next()
with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
tar.extract(t, TEMPDIR)
with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
tar.extractfile(t).read()
class MiscReadTestBase(CommonReadTest): class MiscReadTestBase(CommonReadTest):
def requires_name_attribute(self): def requires_name_attribute(self):

View File

@ -33,6 +33,9 @@ Core and Builtins
Library Library
------- -------
- Issue #24259: tarfile now raises a ReadError if an archive is truncated
inside a data segment.
What's New in Python 3.5.0 beta 3? What's New in Python 3.5.0 beta 3?
================================== ==================================