Unbounded Per-Connection Queue Growth in WebSocketServerExtensionHandler Leads to Denial of Service
Summary RtspMethods.valueOf() silently strips trailing control bytes (any character with code point <= 0x20, the full range that String.trim() removes) before performing a cache lookup against its ten pre-populated method constants. A wire-delivered RTSP request whose method token ends with a trailing control byte — for example PLAY\x00 or PLAY\r, immediately before the separating space — is decoded by RtspDecoder as a fully successful PLAY request, with decoderResult().isSuccess() == true and request.method() == RtspMethods.PLAY (same object reference as the cached singleton). The application layer cannot distinguish this from a clean PLAY request. This is the same root cause as #16723 and #16971, in a sibling that those fixes did not reach. The fix for HttpMethod hardened HttpMethod.valueOf() directly, but RtspMethods.valueOf() has its own independent checkNonEmptyAfterTrim() call that runs before the cache lookup — meaning a trailing-control-byte token hits the cache before the hardened HttpMethod constructor ever sees it. Reproduction Minimal wire-level reproduction Send the following raw bytes to any Netty-based RTSP server using R
Summary Netty skips strict chunk size line validation when the line has no chunk extension (;), so a chunk size line containing an embedded bare CR (e.g. 0\rX) is accepted instead of rejected, enabling HTTP request smuggling. Details io.netty.handler.codec.http.HttpObjectDecoder#checkChunkExtensions only runs the strict validator HttpChunkLineValidatingByteProcessor when a ; is present: java int extensionsStart = line.bytesBefore((byte) ';'); if (extensionsStart == -1) { return; } According to RFC 9112 https://datatracker.ietf.org/doc/html/rfc9112#appendix-A chunk-size = 1HEXDIG PoC java @Test public void test() { String requestStr = "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + "0\rX\r\n" + "\r\n" + "GET /smuggled HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: 0\r\n" + "\r\n"; EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, Ch
Netty split Transfer-Encoding fields bypass final-chunked validation and enable request smuggling
A public GitHub Security Advisory (GHSA-3jrc-fchc-59pw) describes the following issue:
Summary
pov: pov-test
Netty's HTTP/1.1 decoder added RFC 9112 validation for malformed Transfer-Encoding order in the fix family for GHSA-38f8-5428-x5cv / CVE-2026-42585. That validation rejects the single-field case Transfer-Encoding: chunked, identity, but it can be bypassed when equivalent ordering is expressed as multiple header fields and the last field value is a short non-final transfer coding such as gzip or deflate.
For example:
text Transfer-Encoding: chunked Transfer-Encoding: gzip
is equivalent to the combined field value:
text Transfer-Encoding: chunked, gzip
In that value, chunked is not the final transfer coding. A request recipient must reject this message under RFC 9112 framing rules. Netty instead treats the message as chunked, accepts the terminating zero-size chunk, and parses a following request as a second request on the same connection.
This creates an HTTP request-smuggling primitive when Netty is paired with an intermediary or peer that rejects, buffers, or differently interprets the same invalid transfer-coding order.
This should be triaged as a current supported-release bypass/incomplete fix in the malformed Transfer-Encoding family. It is not a claim that malformed split Transfer-Encoding risk was never discussed publicly: Netty issue #9861 raised closely related behavior in 2019, and the May 2026 GHSA-38f8-5428-x5cv advisory fixed a single-field instance of the same ordering rule. The current issue is that latest supported releases still accept the split-field form after those fixes.
Technical Details
HttpUtil.isTransferEncodingChunked(...) returns true if any Transfer-Encoding field contains the comma-separated token chunked:
text codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java:362 codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java:363
HttpHeaders.containsValue(...) checks comma-separated values across header fields:
text codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java:1600 codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java:1603
Once any chunked value is found, HttpObjectDecoder enters chunked decoding. It then validates only the final Transfer-Encoding field value:
text codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:853 codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:859 codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:861
The validation has a length guard:
text codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:863 codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:865 codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java:867
The comment says that if the final value is not longer than chunked, Netty knows it is only chunked. That assumption is false for final values like gzip and deflate. As a result:
- Transfer-Encoding: chunked, identity is rejected. - Transfer-Encoding: chunked followed by Transfer-Encoding: gzip is accepted. - Transfer-Encoding: chunked followed by Transfer-Encoding: deflate is accepted.
Git blame ties this validation block to the May 4, 2026 hardening commit:
text 977661f71f7511ad68ca17cabd7b5185efb978f4 Motivation: RFC9112 specified that chunked must be the last encoding so we should verify this.
PoC
Run the local PoV:
fish cd oss-zero-day-harness bash submission-bundle/netty-pov-test-transfer-encoding-order-bypass/poc/reproduce.sh
The key request shape is:
text POST /one HTTP/1.1\r\n Host: target\r\n Transfer-Encoding: chunked\r\n Transfer-Encoding: gzip\r\n \r\n 0\r\n \r\n GET /two HTTP/1.1\r\n Host: target\r\n \r\n
Observed on current 4.2:
text CASE splitfieldschunkedthengzipaccepted request uri=/one result=success transferEncoding=[chunked, gzip] lastContent bytes=0 result=success request uri=/two result=success transferEncoding=[] lastContent bytes=0 result=success
The same run confirms the patched single-field case is rejected:
text CASE singlefieldchunkedthenidentityrejected request uri=/one result=failure(java.lang.IllegalArgumentException:chunked must be the last encoding present in the Transfer-Encoding header) transferEncoding=[chunked, identity]
The supplementary Content-Length case is rejected on current 4.2:
text CASE splitfieldschunkedthengzipwithcontentlengthrejected request uri=/one result=failure(io.netty.handler.codec.http.ContentLengthNotAllowedException:Content-Length are not allowed in HTTP/1.1 messages that contains a Transfer-Encoding header.) transferEncoding=[chunked, gzip]
Impact
HTTP request smuggling can bypass front-end routing and access-control decisions, desynchronize request processing, or cause a following request to be processed in an unintended context. The exploitability of this primitive depends on a deployment with a parser differential, but Netty is the component that accepts the malformed framing and splits the stream into two requests.
The included PoV also checks a Content-Length variant. Current Netty rejects split Transfer-Encoding plus Content-Length with ContentLengthNotAllowedException. This report therefore does not claim the exact CL.TE exploitation shape from GHSA-38f8-5428-x5cv; it claims the TE-only split-field final-coding bypass that remains in current supported releases.
The PoV is local-only. It uses EmbeddedChannel and HttpRequestDecoder; it does not contact any live service.
Suggested Fix
Parse Transfer-Encoding as one ordered list across all field lines before deciding whether to enter chunked decoding. The decoder should reject HTTP/1.1 requests when the final transfer-coding to
[truncated]
Affected: - maven:io.netty:netty-codec-http affected >=4.2.12.Final; fixed unknown - maven:io.netty:netty-codec-http affected >=4.1.132.Final; fixed unknown
Fixed versions: see advisory
Advisory: https://github.com/netty/netty/security/advisories/GHSA-3jrc-fchc-59pw