Skip to content

Commit 9f07ef7

Browse files
committed
gh-137396: Raise InvalidHeaderError when offset or numbytes is negative
1 parent 8665769 commit 9f07ef7

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

Lib/tarfile.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,13 @@ def frombuf(cls, buf, encoding, errors):
13231323
numbytes = nti(buf[pos + 12:pos + 24])
13241324
except ValueError:
13251325
break
1326+
1327+
if offset < 0:
1328+
raise InvalidHeaderError("invalid sparse header: negative offset")
1329+
1330+
if numbytes < 0:
1331+
raise InvalidHeaderError("invalid sparse header: negative numbytes")
1332+
13261333
structs.append((offset, numbytes))
13271334
pos += 24
13281335
isextended = bool(buf[482])
@@ -1438,6 +1445,13 @@ def _proc_sparse(self, tarfile):
14381445
numbytes = nti(buf[pos + 12:pos + 24])
14391446
except ValueError:
14401447
break
1448+
1449+
if offset < 0:
1450+
raise InvalidHeaderError("invalid sparse header: negative offset")
1451+
1452+
if numbytes < 0:
1453+
raise InvalidHeaderError("invalid sparse header: negative numbytes")
1454+
14411455
if offset and numbytes:
14421456
structs.append((offset, numbytes))
14431457
pos += 24

Lib/test/test_tarfile.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4754,6 +4754,100 @@ class OffsetValidationTests(unittest.TestCase):
47544754
# padding: 255 bytes
47554755
+ tarfile.NUL * 255
47564756
)
4757+
invalid_gnu_sparse_header = (
4758+
# name: 100 bytes
4759+
tarfile.NUL * tarfile.LENGTH_NAME
4760+
# mode, null terminator: 8 bytes
4761+
+ b"0000755" + tarfile.NUL
4762+
# uid: 8 bytes
4763+
+ b"0000000" + tarfile.NUL
4764+
# gid: 8 bytes
4765+
+ b"0000000" + tarfile.NUL
4766+
# size, space: 12 bytes
4767+
+ b"00000000010" + SPACE
4768+
# mtime, space: 12 bytes
4769+
+ b"00000000000" + SPACE
4770+
# chksum: 8 bytes
4771+
+ b" 022014" + tarfile.NUL
4772+
# type: 1 byte (GNU sparse)
4773+
+ tarfile.GNUTYPE_SPARSE
4774+
# linkname: 100 bytes
4775+
+ tarfile.NUL * tarfile.LENGTH_LINK
4776+
# magic: GNU format (8 bytes)
4777+
+ tarfile.GNU_MAGIC
4778+
# uname: 32 bytes
4779+
+ tarfile.NUL * 32
4780+
# gname: 32 bytes
4781+
+ tarfile.NUL * 32
4782+
# devmajor: 8 bytes
4783+
+ tarfile.NUL * 8
4784+
# devminor: 8 bytes
4785+
+ tarfile.NUL * 8
4786+
# prefix: 41 bytes
4787+
+ tarfile.NUL * 41
4788+
# sparse entry 1: offset=-1, numbytes=-1
4789+
+ b"\xff" * 12 + b"\xff" * 12
4790+
# other 3 sparse entries zero-filled
4791+
+ tarfile.NUL * (24 * 3)
4792+
# isextended: 1 byte (0 or 1)
4793+
+ b"\x00"
4794+
# origsize: 12 bytes
4795+
+ tarfile.NUL * 12
4796+
# padding: 17 bytes
4797+
+ tarfile.NUL * 17
4798+
)
4799+
invalid_gnu_sparse_extended = (
4800+
# name: 100 bytes
4801+
tarfile.NUL * tarfile.LENGTH_NAME
4802+
# mode, null terminator: 8 bytes
4803+
+ b"0000755" + tarfile.NUL
4804+
# uid: 8 bytes
4805+
+ b"0000000" + tarfile.NUL
4806+
# gid: 8 bytes
4807+
+ b"0000000" + tarfile.NUL
4808+
# size, space: 12 bytes
4809+
+ b"00000000010" + SPACE
4810+
# mtime, space: 12 bytes
4811+
+ b"00000000000" + SPACE
4812+
# chksum: 8 bytes
4813+
+ b" 006447" + tarfile.NUL
4814+
# type: 1 byte (GNU sparse)
4815+
+ tarfile.GNUTYPE_SPARSE
4816+
# linkname: 100 bytes
4817+
+ tarfile.NUL * tarfile.LENGTH_LINK
4818+
# magic: GNU format (8 bytes)
4819+
+ tarfile.GNU_MAGIC
4820+
# uname: 32 bytes
4821+
+ tarfile.NUL * 32
4822+
# gname: 32 bytes
4823+
+ tarfile.NUL * 32
4824+
# devmajor: 8 bytes
4825+
+ tarfile.NUL * 8
4826+
# devminor: 8 bytes
4827+
+ tarfile.NUL * 8
4828+
# prefix: 41 bytes
4829+
+ tarfile.NUL * 41
4830+
# sparse map entries: up to 4 * 24 bytes
4831+
# entry 1: offset=1, numbytes=1
4832+
+ b"\x80" + b"\x00" * 10 + b"\x01" + b"\x80" + b"\x00" * 10 + b"\x01"
4833+
# Remaining 3 entries zero-filled
4834+
+ tarfile.NUL * (24 * 3)
4835+
# isextended: 1 byte (0 or 1)
4836+
+ b"\x01"
4837+
# origsize: 12 bytes
4838+
+ tarfile.NUL * 12
4839+
# padding: 17 bytes
4840+
+ tarfile.NUL * 17
4841+
) + (
4842+
# sparse entry 1: 24 bytes
4843+
b"\xff" * 12 + b"\xff" * 12
4844+
# Remaining 20 entries zero-filled: 480 bytes
4845+
+ tarfile.NUL * (24 * 20)
4846+
# isextended: 1 byte (0 or 1)
4847+
+ b"\x00"
4848+
# padding: 7 bytes
4849+
+ tarfile.NUL * 7
4850+
)
47574851
valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT)
47584852
data_block = b"\xff" * tarfile.BLOCKSIZE
47594853

@@ -4779,6 +4873,8 @@ def test_invalid_offset_header_validations(self):
47794873
("posix", self.invalid_posix_header),
47804874
("gnu", self.invalid_gnu_header),
47814875
("v7", self.invalid_v7_header),
4876+
("gnu_sparse", self.invalid_gnu_sparse_header),
4877+
("gnu_sparse_extension", self.invalid_gnu_sparse_extended)
47824878
):
47834879
with self.subTest(format=tar_format):
47844880
self._write_buffer(invalid_header)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`tarfile.Tarfile.fromtarfile` and :func:`tarfile.TarInfo.frombuf` raises
2+
when offset or numbytes field in sparse entry or is negative.

0 commit comments

Comments
 (0)