Commit 0357268d authored by Lars Gustäbel's avatar Lars Gustäbel

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

parent 54630d99
...@@ -225,7 +225,7 @@ def calc_chksums(buf): ...@@ -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): ...@@ -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): ...@@ -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
...@@ -2132,9 +2135,9 @@ class TarFile(object): ...@@ -2132,9 +2135,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()
...@@ -2244,8 +2247,13 @@ class TarFile(object): ...@@ -2244,8 +2247,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:
......
...@@ -349,6 +349,29 @@ class CommonReadTest(ReadTest): ...@@ -349,6 +349,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):
......
...@@ -66,6 +66,9 @@ Core and Builtins ...@@ -66,6 +66,9 @@ Core and Builtins
Library Library
------- -------
- Issue #24259: tarfile now raises a ReadError if an archive is truncated
inside a data segment.
- Issue #24552: Fix use after free in an error case of the _pickle module. - Issue #24552: Fix use after free in an error case of the _pickle module.
- Issue #24514: tarfile now tolerates number fields consisting of only - Issue #24514: tarfile now tolerates number fields consisting of only
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment