Repository navigation
tarfile.StreamError: seeking backwards is not allowed due to unskipped block with bad checksum #130577
Description
Bug report
Bug description:
I am reading tar blocks from a stream. The stream can be resumed from an arbitrary position, therefore I am using tarfile.open with ignore_zeros=True. This works most of the time, but sometimes resuming causes a tarfile.StreamError: seeking backwards is not allowed. After inspection, it seems that this is caused by blocks passing the checksum test even though they are invalid. This causes wrong metadata to be parsed, which in turn causes the stream to crash.
Below is a minimal working example with a problematic block. This is a real block in my stream which is yielded from an archive created with tarfile. The archive is not corrupted, and when streaming from the beginning of the archive, everything works fine.
import io
import tarfile
buf = b'\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\xfe\xff\xfe\xff\xff\xff\xff\xff\x01\x00\x01\x00\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xfe\xff\xfd\xff\xfe\xff\xff\xff\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\xfe\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\xfe\xff\xfe\xff\xff\xff\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\xff\xfe\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xfe\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\xfe\xff\xfe\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xfe\xff\xff\xff\x00\x00\xff\xff\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00'
f = io.BytesIO(buf)
with tarfile.open(mode="r|", fileobj=f, ignore_zeros=True) as tar:
for member in tar:
pass # tarfile.StreamError: seeking backwards is not allowedThe backward seeking is caused by bad metadata parsing when calling tarfile.TarInfo.frombuf internally on the block.
import tarfile
buf = b'\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\xfe\xff\xfe\xff\xff\xff\xff\xff\x01\x00\x01\x00\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xfe\xff\xfd\xff\xfe\xff\xff\xff\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\xfe\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\x00\x00\x01\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xff\xff\xfe\xff\xff\xff\x00\x00\x01\x00\x00\x00\xff\xff\xff\xff\x00\x00\xfe\xff\xfe\xff\xff\xff\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\xff\xfe\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xfe\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\xfe\xff\xfe\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\xfe\xff\xff\xff\xfe\xff\xff\xff\x00\x00\xff\xff\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00'
info = tarfile.TarInfo.frombuf(buf, encoding="utf-8", errors="surrogateescape")
print(info.size) # -4722366482873940180736Tested with Python 3.12.8 and 3.13.2.
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Linked PRs
- gh-130577: tarfile now validates archives to ensure member offsets are non-negative #137027
- [3.14] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137169
- [3.13] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137170
- [3.12] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137171
- [3.11] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137172
- [3.10] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137176
- [3.9] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137177
- [3.10] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137644
- [3.9] gh-130577: tarfile now validates archives to ensure member offsets are non-negative (GH-137027) #137645
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
- StatusShow more project fieldsDone
Activity
aeurielesn commentedon Jul 22, 2025
After having a look at the GNU tar code, I can see offsets must be non-negative numbers. I will put together a PR for this one.
pythongh-130577: tarfile now validates archives to ensure member offs…
aeurielesn commentedon Jul 22, 2025
PR is up for review.
gh-130577: tarfile now validates archives to ensure member offsets ar…
pythongh-130577: tarfile now validates archives to ensure member offs…
pythongh-130577: tarfile now validates archives to ensure member offs…
pythongh-130577: tarfile now validates archives to ensure member offs…
pythongh-130577: tarfile now validates archives to ensure member offs…
27 remaining items
frenzymadness commentedon Sep 12, 2025
Am I correct when I think that versions of Python before this commit: ac3d137 are not vulnerable because the
ntifunction did not support negative numbers, and therefore a header cannot contain a negative number leaving to a negativeseekcall?[3.9] gh-130577: tarfile now validates archives to ensure member offs…
hugovk commentedon Sep 15, 2025
release-blockertriage: I see lots of merged backports, is this fixed or is there more to do?encukou commentedon Sep 15, 2025
@frenzymadness: That would be Python 2.6 and below, right? Don't think I can give an "official" security-related answer quickly. Catch me next week if you want explore this together.
@hugovk: Looks like we're good!
frenzymadness commentedon Sep 15, 2025
The commit is tagged in a way that suggests that it's included in 2.7, but it was removed in one weird merge after, so this applies basically to versions below 3.2.
Let's discuss this later.
CVE-2025-8194 python/cpython#130577
CVE-2025-8194 python/cpython#130577
[CVE-2025-8194] tarfile now validates archives to ensure member offse…
[CVE-2025-8194] tarfile now validates archives to ensure member offse…
[CVE-2025-8194] tarfile now validates archives to ensure member offse…
[CVE-2025-8194] tarfile now validates archives to ensure member offse…
CVE-2025-8194 python/cpython#130577