Summary
Netty: HttpObjectDecoder skips arbitrary initial control characters when only initial CRLF characters are permitted
Full technical description
Before reading the first request-line, HttpObjectDecoder skips every byte for whichCharacter.isISOControl(b) is true (0x00–0x1F and 0x7F) as well as all whitespace.
RFC 9112 §2.2 only asks servers to ignore empty CRLF lines preceding the request-line ,
a carefully scoped robustness allowance intended to handle HTTP/1.0 POST workarounds.
Silently absorbing NUL bytes, SOH, STX, and other non-CRLF control characters goes
significantly beyond this, and can be exploited for request-boundary confusion in pipelined
or multiplexed transports where a front-end component treats those bytes differently.
Affected Code
| File | Lines | Role |
|---|---|---|
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1298–1313 | ISO_CONTROL_OR_WHITESPACE static initialiser, marks all ISO control chars |
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1307–1313 | SKIP_CONTROL_CHARS_BYTES ByteProcessor, skips the entire set |
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java |
1275–1289 | LineParser.skipControlChars, advances readerIndex past all matching bytes |
Specification Analysis
RFC 9112 §2.2, Message Parsing
In the interest of robustness, a server that is expecting to receive and parse a
request-line SHOULD ignore at least one empty line (CRLF) received prior to the
request-line.
An HTTP/1.1 user agent MUST NOT preface or follow a request with an extra CRLF.
Deviation
The RFC names a single permitted exception: an empty line (bare CRLF, i.e. the two-byte
sequence \r\n). The ISO_CONTROL_OR_WHITESPACE table is initialised as:
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
ISO_CONTROL_OR_WHITESPACE[128 + b] =
Character.isISOControl(b) || isWhitespace(b);
}
Character.isISOControl returns true for 0x00–0x1F and 0x7F. This includes NUL
(0x00), SOH (0x01), STX (0x02), BEL (0x07), DEL (0x7F), and every other non-CRLF
control character. The SKIP_CONTROL_CHARS state runs this scan unconditionally before the
first READ_INITIAL, meaning any sequence of such bytes prepended to a request is silently
consumed.
A load balancer or TLS terminator that does not perform the same scan sees a different
message boundary than Netty does, which is the basis of a request-desync / smuggling attack.
Suggested Unit Test
Add to HttpRequestDecoderTest.java.
@Test
public void testNonCrlfControlBytesPrecedingRequestLineAreRejected() {
// RFC 9112 §2.2: servers SHOULD ignore "at least one empty line (CRLF)" before the
// request-line. Non-CRLF control bytes are not part of this robustness allowance
// and must not be silently swallowed.
EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder());
ByteBuf buf = Unpooled.buffer();
buf.writeByte(0x00); // NUL , not an empty CRLF line
buf.writeByte(0x01); // SOH , not an empty CRLF line
buf.writeCharSequence(
"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n",
CharsetUtil.US_ASCII);
channel.writeInbound(buf);
HttpRequest req = channel.readInbound();
// Current behaviour: NUL and SOH are in ISO_CONTROL_OR_WHITESPACE, so they are
// silently skipped; the request decodes successfully and isFailure() == false.
//
// RFC-correct behaviour: only empty CRLF lines should be ignored; NUL/SOH must
// cause a parse error, isFailure() == true.
assertTrue(
req.decoderResult().isFailure(),
"Non-CRLF control bytes before the request-line must not be silently skipped " +
"(RFC 9112 §2.2 allows only empty CRLF lines)");
assertFalse(channel.finish());
}
Current behaviour (unfixed): skipControlChars advances past 0x00 and 0x01 because
both are in ISO_CONTROL_OR_WHITESPACE; the request parses normally, isFailure() isfalse → test fails.
Expected behaviour after fix: only CRLF empty lines are tolerated; non-CRLF control
bytes produce an error, isFailure() is true → test passes.
Impact
CVE-2026-50020 has a CVSS score of 5.3 (Medium). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (4.2.15.Final, 4.1.135.Final); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
io.netty:netty-codec-http to 4.2.15.Final or later; io.netty:netty-codec-http to 4.1.135.Final or later
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-50020? CVE-2026-50020 is a medium-severity security vulnerability in io.netty:netty-codec-http (maven), affecting versions >= 4.2.0.Final, <= 4.2.14.Final. It is fixed in 4.2.15.Final, 4.1.135.Final.
- How severe is CVE-2026-50020? CVE-2026-50020 has a CVSS score of 5.3 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of io.netty:netty-codec-http are affected by CVE-2026-50020? io.netty:netty-codec-http (maven) versions >= 4.2.0.Final, <= 4.2.14.Final is affected.
- Is there a fix for CVE-2026-50020? Yes. CVE-2026-50020 is fixed in 4.2.15.Final, 4.1.135.Final. Upgrade to this version or later.
- Is CVE-2026-50020 exploitable, and should I be worried? Whether CVE-2026-50020 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-50020 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-50020?
- Upgrade
io.netty:netty-codec-httpto 4.2.15.Final or later - Upgrade
io.netty:netty-codec-httpto 4.1.135.Final or later
- Upgrade