Skip to content

2.7.18.16: httplib Content-Length / chunk-size validation (CVE-2026-103995) - #95

Merged
icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-httplib-length-chunk
Oct 28, 2026
Merged

icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-httplib-length-chunk

Conversation

@icanhasmath

Copy link
Copy Markdown

Backport of python/cpython pythongh-150751 (pythonGH-150752 + pythonGH-159156; CVE-2026-103995, PSF-2026-47) to 2.7.18.x. This is the same fix as in 3.10.22.1 (#87) and 3.9.25.1 (#88).

Problem: httplib.HTTPResponse parsed Content-Length with int() and chunk sizes with int(_, 16). Both accept values the RFC 9112 grammar forbids: a sign, whitespace, and 0x in base 16. When httplib and a peer disagree about where a message ends, that mismatch can be used for response splitting.

Fix: values are checked against 1*DIGIT / 1*HEXDIG before conversion.

  • An invalid Content-Length is ignored, and the body is read until the connection closes.
  • An invalid chunk size closes the connection with IncompleteRead.
  • Optional whitespace around the Content-Length value is still accepted, per RFC 9112 §5.1.

Behavior change: malformed lengths and chunk sizes such as +5 or 0x10 are now rejected. Well-behaved servers never send these.

Tests: test_malformed_content_length, test_content_length_with_whitespace and test_malformed_chunk_size. The malformed-input tests fail on 2.7.18.15. With the fix, test_httplib passes 58/58, and test_urllib2, test_urllib2_localnet, test_xmlrpc, test_urllib, test_httpservers and test_wsgiref all pass.

Part of 2.7.18.16. It targets the 2.7.18.16 integration branch.

…2026-103995)

Backport of python/cpython pythonGH-150752 and pythonGH-159156 (pythongh-150751; the
3.10.22.1 ports are 833a27a and dbefda3).

HTTPResponse parsed Content-Length with int() and chunk sizes with
int(_, 16).  Both accept input the RFC 9112 grammar forbids: a sign
("+5", "-5"), surrounding whitespace, and for base 16 an "0x" prefix.
A peer that disagrees with httplib about message framing can use this
for response splitting / desynchronisation.  Values are now matched
against Content-Length = 1*DIGIT and chunk-size = 1*HEXDIG first; an
invalid Content-Length is ignored (the body is read to close) and an
invalid chunk size closes the connection with IncompleteRead.  Optional
whitespace (SP/HTAB) around the Content-Length value is still allowed,
per RFC 9112 section 5.1.

2.7.18.x: re has no fullmatch, so the patterns are anchored with \Z and
used with match().  2.7's int() never accepted underscores, but the
grammar check covers them anyway.  Tests use plain loops instead of
subTest and also cover "0x5" as a Content-Length.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@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. This is upstream pythonGH-150752 plus its follow-up pythonGH-159156 (optional whitespace around Content-Length), carried over in full. Nothing later in upstream touches this code.

  • Repro (canned responses through HTTPResponse):

    input base this branch
    Content-Length: +5 length 5 ignored, read to close
    Content-Length: 5 (OWS) length 5 length 5
    0x5 / -1 ignored ignored
    chunk size +5 / 0x5 accepted (hello) IncompleteRead
    chunk size -0 accepted (empty body) IncompleteRead
    chunk size 5 hello hello
  • Tests: test_httplib: 58 run, OK (12 skipped: this build has no _ssl).

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