Skip to content

gh-158181: tarfile: raise ReadError instead of IndexError on a truncated GNU sparse header - #158183

Open
not-ekalabya wants to merge 2 commits into
python:mainfrom
not-ekalabya:fix-tarfile-sparse-indexerror
Open

not-ekalabya wants to merge 2 commits into
python:mainfrom
not-ekalabya:fix-tarfile-sparse-indexerror

Conversation

@not-ekalabya

Copy link
Copy Markdown

Description

Fixes #158181.

_proc_sparse() reads the extended header block for a GNU
GNUTYPE_SPARSE member with a plain tarfile.fileobj.read(BLOCKSIZE),
without checking for a short or empty result the way most other truncated-
data reads in this module do (see copyfileobj's len(buf) < bufsize
check for the same pattern). A truncated archive whose sparse member has
isextended set makes the parse loop call nti() on a short/empty buffer,
and nti() raises IndexError (s[0] on empty input) rather than the
documented tarfile.ReadError, escaping every except/try in
next()/TarFile.__init__ that's written against the documented contract.

This raises ReadError("unexpected end of data") directly when the read is
short, matching the exact pattern already used at
copy_or_raise/copyfileobj and several other call sites in this module.

Found via

Found via an automated code-review pass using GLM-5.3-flash paired with
scopegrep, a semantic code-retrieval tool; independently re-verified and
fixed by hand.

Testing

I don't have a build of this checkout's CPython (this environment's
installed interpreter is 3.12, and main's stdlib already uses syntax/
builtins 3.12 can't parse -- e.g. frozendict), so I could not run
Lib/test/test_tarfile.py directly. I verified the fix with a standalone
harness that loads Lib/tarfile.py via importlib against the system's
already-imported re/shutil (to sidestep the _sre version mismatch),
with a minimal shim for the one new builtin the module needs at import
time:

  • Before the fix, a hand-built truncated GNU sparse ('S' typeflag,
    isextended=1, no extended header block following) raised
    IndexError: index out of range from nti(), reproducing the report.
  • After the fix, the same input raises
    tarfile.ReadError: unexpected end of data.

Please run the real test suite (particularly Lib/test/test_tarfile.py's
sparse-file tests) as part of review, since I could not do so myself here.

Drafted with Claude Sonnet 5 (Anthropic); reviewed by @not-ekalabya before merge.

… sparse header

_proc_sparse() read the extended header block for a GNU sparse member
with a plain read(BLOCKSIZE) instead of checking for a short/empty
result, so a truncated archive whose sparse member has isextended set
made nti() raise IndexError (s[0] on an empty buffer) instead of the
documented ReadError. Raise ReadError directly when the read is
short, matching the pattern used elsewhere in this module for
truncated data.
@python-cla-bot

python-cla-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@BHUVANSH855 BHUVANSH855 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kindly sign the CLA, so that maintainers can begin with review process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tarfile: IndexError instead of ReadError on a truncated GNU sparse member's extended header

2 participants