Skip to content

2.7.18.16: tarfile streaming-mode seek loops at EOF (CVE-2026-11972) - #92

Merged
icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-tarfile-stream-eof
Oct 28, 2026
Merged

icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-tarfile-stream-eof

Conversation

@icanhasmath

Copy link
Copy Markdown

Backport of python/cpython pythonGH-151982 / pythongh-151981 (CVE-2026-11972, PSF-2026-31) to 2.7.18.x.

Problem: in streaming mode (mode="r|"), tarfile._Stream.seek() reads forward by the declared number of blocks, and it keeps going after EOF. A header that declares a huge member size (1 << 64) makes getmembers() loop for an effectively unbounded time.

Fix: stop the loop once read() returns no data. The next header read then raises ReadError("unexpected end of data").

Test: StreamReadTest.test_getmembers_big_size covers both GNU and PAX formats. It hangs on 2.7.18.15 (killed by a timeout) and passes with the fix. test_tarfile passes against OpenSSL 1.1.1w on Linux.

This is one of the 2.7.18.16 fixes. Each one targets the 2.7.18.16 integration branch, which is cut from 2.7.18.15 (#91). The release PR will take 2.7.18.16 into 2.7.

Backport of python/cpython pythonGH-151982 (3.13 3f031d4).

In streaming mode (mode="r|"), _Stream.seek() skipped forward by reading
the declared number of blocks even after the stream hit EOF.  A member
header that declares a huge size (e.g. 1 << 64 via a GNU/PAX header)
made getmembers() spin for an effectively unbounded time.  seek() now
stops reading once read() returns no data, and the following header read
raises ReadError("unexpected end of data").

2.7.18.x: xrange() loop; the test loops over GNU_FORMAT and PAX_FORMAT
instead of using support.subTests, and lives in StreamReadTest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ezequielp-activestate ezequielp-activestate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No blockers.

  • Fix matches upstream. _Stream.seek() now stops at EOF, the same change as upstream pythonGH-151982 (f50bf13). No later upstream fix touches this loop.
  • Repro: a streaming-mode (r|) archive whose single header declares a size of 1 << 64 (GNU) or (1 << 64) - 1 (PAX), followed by one zero block:
    • 2.7.18.16 base: getmembers() still running after 10 s, killed by an alarm.
    • This branch: ReadError: unexpected end of data immediately, for both formats.
  • Tests: test_tarfile passes on this branch (Python 2.7.18 built from the 2.7.18.16 branch).

@icanhasmath
icanhasmath merged commit 9ce1f67 into 2.7.18.16 Oct 28, 2026
icanhasmath added a commit that referenced this pull request Oct 28, 2026
Bump PY_VERSION to 2.7.18.16 and add release notes for the security
fixes merged into 2.7.18.16 (#92-#108, #111): tarfile (CVE-2026-11972),
csv.Sniffer (CVE-2026-18503), ftplib.ftpcp (CVE-2026-8328), httplib
(CVE-2026-103995, CVE-2021-3737 and GHSA-w4q2-g22w-6fr4),
SimpleHTTPServer (CVE-2021-28861), mimetypes on Windows (CVE-2024-3220),
pyexpat (CVE-2026-4224), bz2 (CVE-2026-9669), ElementPath
(CVE-2026-6879), urllib2 (CVE-2026-15806), HTMLParser (CVE-2026-15308),
bundled Expat 2.9.0 (30 CVEs), the Expat hash salt (CVE-2026-7210), the
ssl SNI callback (CVE-2026-19445), unicodedata (CVE-2026-3276) and
os.mkdir on Windows (CVE-2024-4030), plus the review follow-up build
fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants