Go 1.24.2 rejects bare LF in chunk-size lines
April's HTTP security fix removes a parsing discrepancy in chunked transfer encoding. The change matters when intermediaries disagree about where a chunk extension ends.
Request smuggling can begin with two implementations assigning different meanings to the same byte. Go's April security patch concerns a line feed in chunked transfer encoding. Go accepted an invalid chunk-size line ending with a bare LF, while another server or proxy could interpret that LF as part of a chunk extension. A disagreement inside the body framing could then become a disagreement about request boundaries.
Go 1.24.2, released on April 1, rejects these invalid lines. Go 1.23.8 includes the same security correction. The release announcement identifies CVE-2025-22871 and describes the combination with an incorrectly interpreting intermediary as the condition that can permit request smuggling. That qualification matters. Accepting malformed input is the Go defect, while the attack depends on how another participant interprets it.
I like Go's preference for visible behavior, and protocol parsing needs that same precision. Application handlers should receive a request whose framing has an unambiguous interpretation. A handler cannot recover a boundary that two network participants already understood differently.
Chunk sizes establish body boundaries
RFC 9112's chunked transfer coding frames a body as chunks with hexadecimal sizes. Each size line ends with CRLF, followed by the specified amount of data and another CRLF. A zero-sized chunk ends the chunk sequence, with trailer handling completing the message. Extensions can accompany the size line, but their presence does not remove its framing requirements.
This structure lets a sender stream a body without knowing its total size in advance. It also gives every parser repeated opportunities to disagree about where framing ends and content begins. A byte interpreted as a terminator by one participant can remain extension data for another. Subsequent bytes then acquire different roles in the two parsers.
The issue is consequently different from an application receiving an unusual string value. Framing determines which bytes even belong to the request. Filtering a decoded field later cannot make the earlier network participants agree about where that request stopped. The correction belongs where the chunked reader recognizes its control lines.
The reader now validates the terminator
The tagged Go 1.24.2 chunked reader reads through LF, then verifies the relationship between CR and the end of that line. An absent CR produces the error chunked line ends with bare LF. A CR anywhere other than the position immediately before the final LF produces invalid CR in chunked line.
Only after validating those positions does the function remove the terminating CRLF. That order keeps malformed framing from entering later transformations. A routine that strips extensions or trims whitespace should operate on a line whose boundary has already passed validation. Otherwise, discarding characters can erase the evidence that its framing was invalid.
The code comment makes a narrow standards distinction. HTTP header parsing can permit a bare LF under RFC 9112's robustness rules, but that permission does not extend to chunked encoding lines. Applying a general-purpose notion of acceptable line endings to both contexts would lose the distinction on which this fix depends.
The decoder then trims trailing optional whitespace, removes the chunk extension, and parses the remaining hexadecimal size. Invalid hexadecimal bytes and excessively long numeric sizes have their own errors. These checks address different parts of decoding. The new terminator check does not replace them, and their existence could not compensate for accepting the wrong line boundary.
Extensions remain supported
The patch does not prohibit ordinary chunk extensions. In this implementation, removeChunkExtension discards the part after the semicolon before interpreting the size. That behavior makes the validity of the surrounding line especially relevant. The decoder must know where the line ends before deciding which part of it can be ignored.
The tagged chunked-reader tests include extension-bearing chunks with quoted values and extensions without values. They verify that decoding still returns the intended payload. These cases distinguish legitimate extension support from accepting malformed framing that happens to occur near an extension.
The tests also cover partial reads and truncated streams. A reader can have some data available without having the next complete chunk header, and it should not unnecessarily block before returning that data. At the same time, premature EOF while reading a required chunk component must remain an error. Security changes have to preserve these streaming properties because correct callers depend on them.
There is also an overhead limit in the reader. Chunk extensions can add bytes without adding payload, so the implementation tracks excess framing overhead and rejects an excessive accumulation. That resource check answers a different question from the line-ending fix. A short malformed line can create a parsing discrepancy without exhausting any resource limit.
The relevant unit is the complete connection path
The announcement's reference to a server or proxy explains why a local parser test cannot by itself describe an entire deployment's exposure. A connection path may contain another HTTP parser before or after the Go program. The risk arises when those participants accept the same stream under incompatible interpretations. Fixing Go's handling removes its acceptance of the particular invalid chunk-size line.
This does not require an application-facing API change. Standard-library callers receive the correction through the patched implementation. It can change the treatment of previously accepted malformed peers, which is the intended effect. Conforming senders already use CRLF for these chunk boundaries and do not need an alternative encoding.
I consider rejection appropriate here because permissive recovery would need to preserve agreement with every other parser on the path. The release instead gives the affected chunk-size lines a definite invalid result. The distinction is visible directly in the decoder before it parses a numeric size. Valid extensions and incremental body reads keep their existing roles, while a bare LF no longer ends an accepted chunk-size line in Go's decoder.