summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Gustäbel <lars@gustaebel.de>2015-07-06 07:29:41 (GMT)
committerLars Gustäbel <lars@gustaebel.de>2015-07-06 07:29:41 (GMT)
commite12aa62d689f435b9b75cde51e432d997646d5f9 (patch)
treec4d9a6888955dd0a184c55de513d41a871228997
parent53ecc58bd9da21989ae1811f97248b40e028ccd9 (diff)
parent0357268d96d4ff3546cfd89f594a5630a3adf747 (diff)
downloadcpython-e12aa62d689f435b9b75cde51e432d997646d5f9.zip
cpython-e12aa62d689f435b9b75cde51e432d997646d5f9.tar.gz
cpython-e12aa62d689f435b9b75cde51e432d997646d5f9.tar.bz2
Merge with 3.4: Issue #24259: tarfile now raises a ReadError if an archive is truncated inside a data segment.
-rwxr-xr-xLib/tarfile.py22
-rw-r--r--Lib/test/test_tarfile.py23
-rw-r--r--Misc/NEWS3
3 files changed, 41 insertions, 7 deletions
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index d1279d2..ca45126 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -225,7 +225,7 @@ def calc_chksums(buf):
signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf))
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.
If length is None, copy the entire content.
"""
@@ -240,13 +240,13 @@ def copyfileobj(src, dst, length=None):
for b in range(blocks):
buf = src.read(BUFSIZE)
if len(buf) < BUFSIZE:
- raise OSError("end of file reached")
+ raise exception("unexpected end of data")
dst.write(buf)
if remainder != 0:
buf = src.read(remainder)
if len(buf) < remainder:
- raise OSError("end of file reached")
+ raise exception("unexpected end of data")
dst.write(buf)
return
@@ -690,7 +690,10 @@ class _FileInFile(object):
length = min(size, stop - self.position)
if data:
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:
buf += NUL * length
size -= length
@@ -2150,9 +2153,9 @@ class TarFile(object):
if tarinfo.sparse is not None:
for offset, size in tarinfo.sparse:
target.seek(offset)
- copyfileobj(source, target, size)
+ copyfileobj(source, target, size, ReadError)
else:
- copyfileobj(source, target, tarinfo.size)
+ copyfileobj(source, target, tarinfo.size, ReadError)
target.seek(tarinfo.size)
target.truncate()
@@ -2267,8 +2270,13 @@ class TarFile(object):
self.firstmember = None
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.
- self.fileobj.seek(self.offset)
tarinfo = None
while True:
try:
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 0f03c73..57cddb2 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -364,6 +364,29 @@ class CommonReadTest(ReadTest):
finally:
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):
def requires_name_attribute(self):
diff --git a/Misc/NEWS b/Misc/NEWS
index 2bd6bcb..cec6779 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -17,6 +17,9 @@ Core and Builtins
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?
==================================